Compare commits

...

145 Commits

Author SHA1 Message Date
Simon Vieille 291eaa4f83 update ci 2022-07-26 15:27:56 +02:00
Simon Vieille 794d86f69e update ci 2022-07-26 15:05:44 +02:00
Simon Vieille e61e6880a2 update ci 2022-07-26 15:04:48 +02:00
Simon Vieille 4b81fa715c update ci 2022-07-26 15:04:22 +02:00
Simon Vieille 330ee2a43e update ci 2022-07-26 15:03:53 +02:00
Simon Vieille b246fa0281 update ci 2022-07-26 14:57:19 +02:00
Simon Vieille 3bc214b72e update ci 2022-07-26 14:55:25 +02:00
Simon Vieille 64f77b102d update ci 2022-07-26 14:49:29 +02:00
Simon Vieille 9f5143cc25 update changelog 2022-07-26 14:33:45 +02:00
Simon Vieille 48737c5300 update ci 2022-07-26 14:31:01 +02:00
Simon Vieille 293e4bf62b fix woodpecker pipeline 2022-07-25 11:06:27 +02:00
Simon Vieille 049e601f62 add woodpacker ci 2022-07-25 10:38:59 +02:00
Simon Vieille 16ab6bfece add woodpacker ci 2022-07-25 10:34:24 +02:00
Simon Vieille 7892ce98d2 fix typo 2022-07-05 16:52:03 +02:00
Simon Vieille 2a1e9f5010 release v2.4.1 2022-07-05 14:49:06 +02:00
Simon Vieille 05b1b16cb1 fix user setting save 2022-07-05 14:49:00 +02:00
Simon Vieille 0176ef22d4 update readme 2022-05-31 21:39:55 +02:00
Simon Vieille a8ecf3ea46 release v2.4.0 2022-05-31 21:24:08 +02:00
Simon Vieille 8d1842f622 update changelog 2022-05-31 21:23:53 +02:00
Simon Vieille a0be3757e9 update screenshots 2022-05-31 21:21:30 +02:00
Simon Vieille 7b2f64c5cd add translations 2022-05-31 21:11:11 +02:00
Simon Vieille 6b1a995d5d add tooltips 2022-05-31 16:43:49 +02:00
Simon Vieille 814567635d update changelog 2022-05-31 16:29:29 +02:00
Simon Vieille 32322a0ab2 add button to set default colors 2022-05-31 16:28:43 +02:00
Simon Vieille 95230e9e4a add menu hover effect 2022-05-31 15:48:17 +02:00
Simon Vieille e2430866e5 add button to set default colors 2022-05-31 15:48:02 +02:00
Simon Vieille 9f9f8f90c8 remove focus on opener after click 2022-05-30 19:49:59 +02:00
Simon Vieille 28fc33ee79 change saving progression 2022-05-30 19:49:36 +02:00
Simon Vieille f116b8463a fix menu with categories header 2022-05-30 13:22:03 +02:00
Simon Vieille d8edbae848 fix minor issues 2022-05-30 12:58:49 +02:00
Simon Vieille 4e0c7be2c6 remove deprecated app.php file 2022-05-30 12:04:15 +02:00
Simon Vieille 2cb8c22be0 fix avatar size (nextcloud 24) 2022-05-04 21:05:38 +02:00
Simon Vieille 389da47cab release v2.3.5 2022-04-11 19:11:41 +02:00
Simon Vieille a6a84ee784 update opener active style (fix #99) 2022-04-10 18:56:50 +02:00
Simon Vieille 52bbd49d9a update readme 2022-03-15 11:44:11 +01:00
Simon Vieille c0b1701744 release v2.3.4 2022-03-15 10:56:05 +01:00
Simon Vieille ee98a49366 fix blank line when settings are open 2022-03-15 10:51:40 +01:00
Simon Vieille a67688e779 release v2.3.3 2022-02-07 14:30:08 +01:00
Simon Vieille 9afde0dc9d update changelog 2022-02-07 14:29:51 +01:00
Simon Vieille 0f73ad42c4 fix SQL Exception InvalidFieldNameException (#93) 2022-02-07 14:28:34 +01:00
Simon Vieille 007ac2bb8e hide the scrollbar when mouse is out (menu always displayed) 2022-02-07 14:21:29 +01:00
Simon Vieille 8c5b0e9834 update ci/cd conf 2022-01-14 16:52:36 +01:00
Simon Vieille 59e3b6b85e update ci/cd conf 2022-01-14 16:50:15 +01:00
Simon Vieille a913bd5ba2 update ci/cd conf 2022-01-14 16:49:14 +01:00
Simon Vieille 2d32e542cb update ci/cd conf 2022-01-14 16:46:47 +01:00
Simon Vieille 5d03fbaa49 update ci/cd conf 2022-01-14 16:44:54 +01:00
Simon Vieille 015493fc08 update ci/cd conf 2022-01-13 00:16:56 +01:00
Simon Vieille 992e54ac30 update ci/cd conf 2022-01-13 00:15:20 +01:00
Simon Vieille 67dfb7e833 update screenshots 2022-01-12 14:54:41 +01:00
Simon Vieille 2ae08f7b77 release v2.3.2 2022-01-12 14:12:17 +01:00
Simon Vieille 489321081a fix hidden menu 2022-01-12 14:11:38 +01:00
Simon Vieille 66a1528341 release v2.3.1 2022-01-12 13:30:43 +01:00
Simon Vieille 64b7f3f534 fix #88 2022-01-12 13:29:44 +01:00
Simon Vieille 715dae6ab4 release v2.3.0 2022-01-12 12:51:29 +01:00
Simon Vieille e2a9fecac9 fix typo 2022-01-12 12:48:50 +01:00
Simon Vieille e2d17022f9 fix incorrect html label 2022-01-12 12:46:41 +01:00
Simon Vieille 29d79ad589 update changelog 2022-01-12 12:43:11 +01:00
Simon Vieille 5a4294fad4 Merge branch 'feature/issue82' into develop 2022-01-12 12:41:29 +01:00
Simon Vieille d34b41ba94 add option to keep an app in the top and side menu 2022-01-12 12:40:17 +01:00
Simon Vieille a6016552c9 update changelog and readme 2022-01-12 11:44:45 +01:00
Simon Vieille 50c65811a6 add auto-reload when settings are saved 2022-01-11 22:23:13 +01:00
Simon Vieille f64436f0a3 Merge branch 'feature/issue83' into develop 2022-01-11 20:20:51 +01:00
Simon Vieille 34b1c2809b fix issue with custom categories and add experimental notice 2022-01-11 20:20:44 +01:00
Simon Vieille 5d9dc804f8 add bigger select 2022-01-11 20:02:02 +01:00
Simon Vieille 30072500bc add a way to create custom categories and manage application categories 2022-01-11 19:59:51 +01:00
Simon Vieille a9ca55390d release v2.2.0 2022-01-10 15:02:05 +01:00
Simon Vieille 7230863d0a update icons (#84) 2022-01-10 14:59:17 +01:00
Simon Vieille d9046a205f update dependencies 2022-01-09 22:26:46 +01:00
Simon Vieille 0923a9107c fix categories order 2022-01-09 22:17:01 +01:00
Simon Vieille 1cbdca87df generate default colors using theming colors 2022-01-09 16:43:12 +01:00
Simon Vieille 506fa22f4b update changelog 2021-12-03 14:14:03 +01:00
Simon Vieille a97e94e2e5 release v2.1.0 (NC23 is compatible) 2021-12-03 13:47:52 +01:00
Simon Vieille b6fb1783ba release v2.0.1 2021-11-14 20:05:22 +01:00
Simon Vieille 1b0418a466 fix js error on the personal settings page (undefined sortable) 2021-11-14 20:04:58 +01:00
Simon Vieille d37773cf5f fix js error on the personal settings page (undefined sortable) 2021-11-14 20:04:36 +01:00
Simon Vieille 1aea6ee4f4 fix #78: top menu is broken - invisible apps are shown 2021-11-14 20:01:43 +01:00
Simon Vieille a6dd6854e4 fix issue #77 2021-11-14 19:37:14 +01:00
Simon Vieille 48f403882a update makefile for assets build 2021-11-14 17:32:54 +01:00
Simon Vieille 71b9bc4766 release v2.0.0 2021-11-14 17:17:46 +01:00
Simon Vieille 228f07ea25 Merge branch 'feature/removing-jquery' into develop 2021-11-14 17:03:04 +01:00
Simon Vieille fd2ae59d16 remove background and border from opener (#73) 2021-11-14 16:44:52 +01:00
Simon Vieille 9124de3c81 fix the code of conduct 2021-11-06 20:11:53 +01:00
Simon Vieille 5e308b610d update the build task to include vendor/ 2021-11-05 20:20:42 +01:00
Simon Vieille db2220e745 replace the sortable jquery plugin with html5sortable 2021-11-05 20:19:08 +01:00
Simon Vieille 1143843a5b fix ci/cd conf 2021-11-03 23:02:57 +01:00
Simon Vieille 539a81c07e add ci/cd conf 2021-11-03 20:01:54 +01:00
Simon Vieille d978da2c72 Merge branch 'develop' into feature/removing-jquery 2021-11-03 19:52:55 +01:00
Simon Vieille 544ed1a1fa add ci/cd conf (drone) 2021-11-03 19:52:24 +01:00
Simon Vieille 56e87d470d add ci/cd conf (drone) 2021-11-03 19:17:29 +01:00
Simon Vieille 9173ca37c7 add ci/cd conf (drone) 2021-11-02 20:48:54 +01:00
Simon Vieille 11ccdceb98 update documentation 2021-10-31 19:43:25 +01:00
Simon Vieille 3d3da18322 update metroline conf file 2021-10-31 18:44:30 +01:00
Simon Vieille 3a999b22b0 Merge branch 'develop' into feature/removing-jquery 2021-10-31 17:48:06 +01:00
Simon Vieille e72d0555db update metroline conf file 2021-10-31 17:44:21 +01:00
Simon Vieille 7866089532 add metroline conf file 2021-10-31 17:42:22 +01:00
Simon Vieille ed2531db55 add fetch to make request 2021-10-31 17:27:08 +01:00
Simon Vieille c53221d112 fix indentation 2021-10-31 17:02:09 +01:00
Simon Vieille 9a95151156 remove console log 2021-10-31 16:56:52 +01:00
Simon Vieille 80369a39e1 remove jquery excepted for sorting categories 2021-10-31 16:55:43 +01:00
Simon Vieille 7471e93847 fix null setting update 2021-10-31 16:55:04 +01:00
Simon Vieille d1faf881da add timeout 2021-10-31 16:54:16 +01:00
Simon Vieille 96ffdf93ea remove useless vars 2021-10-29 15:30:22 +02:00
Simon Vieille 6f0c4e98e0 fix top menu generation; fix issue #66 by removing usage of setInterval 2021-10-29 13:56:48 +02:00
Simon Vieille c2e6d24b95 remove ';' on endline 2021-10-29 11:27:45 +02:00
Simon Vieille 5d93d02960 refactoring of alwaysDisplayed function 2021-10-29 11:24:01 +02:00
Simon Vieille a074d747c1 remove most of jquery from top menu 2021-10-29 11:14:27 +02:00
Simon Vieille cc7c9f6ed5 remove most of jquery 2021-10-28 15:47:03 +02:00
Simon Vieille f26adb6ac1 remove jquery from components 2021-10-28 13:50:16 +02:00
Simon Vieille 3b6b8ad54a add missing svg 2021-10-28 13:16:55 +02:00
Simon Vieille 64c4d7a1e1 release v1.28.0 2021-10-21 21:43:30 +02:00
Simon Vieille 3975f3be28 fix #63: add a new side menu with categories 2021-10-21 21:37:32 +02:00
Simon Vieille 1896bb399d fix #62: hide app notification icon 2021-10-21 20:06:17 +02:00
Simon Vieille d77c2ef9ae release v1.27.1 2021-08-11 14:43:52 +02:00
Simon Vieille db0fa60ce8 fix German translation render 2021-08-11 14:43:04 +02:00
Simon Vieille 33d7c6a7f0 release v1.27.0 2021-08-11 11:58:15 +02:00
Simon Vieille a72bb87aa2 hide personal settings access when settings are forced by the administrator 2021-08-11 11:57:43 +02:00
Simon Vieille 5e4a7bcae4 improved German translations 2021-08-11 11:57:01 +02:00
Simon Vieille b900ed7a4d release v1.26.0 2021-08-02 21:48:58 +02:00
Simon Vieille 4a10338588 Merge pull request 'Add Czech localization' (#61) from Pavelb/side_menu:develop into develop
Reviewed-on: https://gitnet.fr/deblan/side_menu/pulls/61
2021-08-02 21:35:34 +02:00
Pavelb 88ecd2ceaf Add Czech localization 2021-07-23 14:45:45 +02:00
Simon Vieille 1dcf1b7938 update changelog 2021-07-09 12:55:05 +02:00
Simon Vieille 143ece3be8 update changelog 2021-07-09 12:54:28 +02:00
Simon Vieille f86ec1de52 update changelog 2021-07-09 12:42:51 +02:00
Simon Vieille b780f53e0b add PHP version as dependency 2021-07-09 12:42:43 +02:00
Simon Vieille 86be38af6c add translations: zh_CN 2021-07-09 11:52:05 +02:00
Simon Vieille 7887a441b3 add compatibility with Nextcloud 22, add CHANGELOG.md, update app icon 2021-07-07 21:35:25 +02:00
Simon Vieille 44478af3a2 release v1.24.0 2021-07-03 20:33:42 +02:00
Simon Vieille d2a8b05772 add background opacity (fix #58) 2021-07-03 20:23:39 +02:00
Simon Vieille fba980416d fix translations 2021-03-02 19:16:53 +01:00
Simon Vieille 95c840ae83 release v1.23.1 2021-03-02 19:16:44 +01:00
Simon Vieille f021c9f0cd fix issue with opener selector 2021-03-02 19:16:37 +01:00
Simon Vieille 58e03e903c release v1.23.0 2021-02-27 14:28:02 +01:00
Simon Vieille 9747820cb8 add dark mode colors 2021-02-27 14:21:55 +01:00
Simon Vieille d8d6aededa add compatibility with nextcloud 21 2021-02-24 15:16:29 +01:00
Simon Vieille a27e723657 Merge branch 'feature/issue55' into develop 2021-02-09 16:51:29 +01:00
Simon Vieille c7db75cea3 release v1.22.2 2021-02-09 16:51:27 +01:00
Simon Vieille 4cd5421e53 fix regression: apps does not open in the tab 2021-02-09 16:50:16 +01:00
Simon Vieille f2d1de1cbe release v1.22.1 2021-02-05 17:57:46 +01:00
Simon Vieille 6be679c569 fix regression: apps does not open in the tab 2021-02-05 17:54:50 +01:00
Simon Vieille 0893ddd5fe release v1.22.0 2021-02-03 15:23:07 +01:00
Simon Vieille c893391065 update ui of admin and fix #53 2021-02-03 15:21:08 +01:00
Simon Vieille b06df8ad9f update ui of admin lists 2021-02-03 10:27:08 +01:00
Simon Vieille 13f7e63514 PSR 2 compliance 2021-02-03 10:15:59 +01:00
Simon Vieille 64da45e676 add feature: order categories 2021-02-03 10:14:03 +01:00
Simon Vieille 77e27bcc1a add code of conduct and update chat room link 2020-12-23 13:39:43 +01:00
55 changed files with 3690 additions and 1844 deletions
+54
View File
@@ -0,0 +1,54 @@
---
kind: pipeline
type: docker
name: default
steps:
- name: dependencies
image: deblan/devenv
commands:
- npm install
- name: build
image: deblan/devenv
commands:
- make npm-build
when:
branch:
exclude:
- master
include:
- develop
- feature/*
event:
- push
- pull_request
- name: release
image: deblan/devenv
volumes:
- name: artifacts
path: /artifacts
environment:
APP_CERTIFICATE:
from_secret: app_certificate
commands:
- mkdir -p "$HOME/.nextcloud/certificates"
- echo "$APP_CERTIFICATE" > "$HOME/.nextcloud/certificates/side_menu.key"
- export VERSION=$(grep "<version>" appinfo/info.xml | grep -o "[0-9]*\.[0-9]*\.[0-9]*" --color=never)
- export RELEASE_DIRECTORY="/artifacts/deblan/side_menu"
- make release
when:
branch:
exclude:
- develop
- feature/*
include:
- master
event:
- push
volumes:
- name: artifacts
host:
path: /var/www/html/artifacts
+40
View File
@@ -0,0 +1,40 @@
pipeline:
dependencies:
image: deblan/devenv
commands:
- npm install
when:
event: [tag, push, pull_request]
build:
image: deblan/devenv
commands:
- make npm-build
when:
event: [push, pull_request]
package:
image: deblan/devenv
volumes:
- /var/www/html/artifacts:/var/www/html/artifacts
secrets: [app_certificate]
commands:
- mkdir -p "$HOME/.nextcloud/certificates"
- echo "$APP_CERTIFICATE" > "$HOME/.nextcloud/certificates/side_menu.key"
- export VERSION=$(grep "<version>" appinfo/info.xml | grep -o "[0-9]*\.[0-9]*\.[0-9]*" --color=never)
- export RELEASE_DIRECTORY="/var/www/html/artifacts/deblan/side_menu"
- make release
when:
event: [tag]
release:
image: plugins/gitea-release
volumes:
- /var/www/html/artifacts:/var/www/html/artifacts
settings:
api_key:
from_secret: gitnet_api_key
base_url: https://gitnet.fr
files: /var/www/html/artifacts/deblan/side_menu/${CI_COMMIT_TAG/v//}/*
when:
event: [tag]
+364
View File
@@ -0,0 +1,364 @@
## [Unreleased]
## 2.4.2
### Fixed
* fix typo
### Changed
* change ci/cd
## 2.4.1
### Fixed
* fix user setting save
## 2.4.0
### Added
* remove focus on opener after click
* add button to set default colors
* add menu hover effect
* add translations
### Fixed
* fix deprecated app.php file
* fix menu with categories header
* fix minor issues
### Changed
* change saving progression
### Removed
* Nextcloud 19 is not supported anymore
* PHP 7.3 is not supported anymore
## 2.3.5
### Fixed
* fix white square (#99)
## 2.3.4
### Fixed
* fix blank line when settings are open (#96)
## 2.3.3
### Added
* hide the scrollbar when mouse is out (menu always displayed)
### Fixed
* fix SQL Exception InvalidFieldNameException (#93)
## 2.3.2
### Fixed
- fix hidden menu
## 2.3.1
### Fixed
- fix #88: does not work with default menu
## 2.3.0
### Added
- fix #82: add an option to keep visible an app in both menus
- fix #83: add custom categories
- add auto-reload when settings are saved
## 2.2.0
### Added
- fix #84: update icons
- fix #85: use Nextcloud colors by default
### Fixed
- fix categories order in large menu
## 2.1.0
### Added
- add compatibility with Nextcloud 23
## 2.0.1
### Fixed
- fix #78: Top menu is broken - invisible apps are shown
- fix #77: Update personal settings - HTTP error 412 (Precondition Failed)
- fix js error on the personal settings page (undefined sortable)
## 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
## 1.27.2
### Fixed
- fix #62: hide app notification icon
## 1.27.1
### Fixed
- fix German translation render
## 1.27.0
### Added
- hide personal settings access when settings are forced by the administrator
### Fixed
- improve German translations
## 1.26.0
### Added
- add Czech translation
## 1.25.2
### Fixed
- fix CHANGELOG
## 1.25.1
### Added
- add PHP version as dependency
- add chinese translation
### Fixed
- fix CHANGELOG
## 1.25.0
### Added
- add compatibility with Nextcloud 22
- add CHANGELOG.md (fix #59)
- update app icon
## 1.24.0
### Added
- add option to define the background opacity (fix #53)
- add missing translations
## 1.23.1
### Fixed
- fix but wih dark mode opener option
## 1.23.0
### Added
- add support of dark Theme and Breeze Dark
- add support of Nextcloud 21
## 1.22.2
### Fixed
- fix regression: apps does not open in new tab (fix #55)
## 1.22.1
### Fixed
- fix regression: apps does not open in new tab
## 1.22.0
### Added
- Add option to sort categories (fix #53)
- Update admin UI
## 1.21.0
### Added
- [FEATURE] Logo in the menu links to main page of installation (#51)
## 1.20.1
### Fixed
- Fix translations
## 1.20.0
### Added
- [FEATURE] Ability to remove apps from the Big Menu (#49)
## 1.19.1
### Fixed
- fix #47: setting for list/grid view in files app flashes and dissapears
## 1.19.0
### Added
- add option: the menu is enabled by default for users (fix #46)
## 1.18.0
### Added
- add option to show link to settings (fix #44)
- refactor menus using several components
## 1.17.0
### Added
- compliance with the app checker
- add an action to export the configuration
- add an action to purge the cache
## 1.16.3
### Fixed
- fix links that must be opened in new window (https://help.nextcloud.com/t/external-petit-probleme-concernant-lapplication-external-sites-ou-sites-externes/94884/11)
## 1.16.2
### Fixed
- fix issue with personal settings when global settings are forced
## 1.16.1
### Fixed
- fix #42: add cache to manage failures to access apps.nextcloud.com
- fix #41: side menu was hover apps list
## 1.16.0
### Added
- add an option to force settings to users (fix #38)
## 1.15.0
### Changed
- New name
### Fixed
- Fix #36: always displayed is not expanding
## 1.14.0
### Added
- add an api accessed by components
- add a config proxy in controllers
### Fixed
- fix translations
## 1.13.0
### Added
- add an option to display the avatar instead of the logo (fix #34)
## 1.12.3
### Added
- add a delay before moving elements (fix #33)
## 1.12.2
### Fixed
- fix #30: `Always displayed` menu can not be close using touchscreens
## 1.12.1
### Fixed
- fix typo
- fix translations
## 1.12.0
### Fixed
- fix #30: administrators and users can select what apps must be opened in new tab
- fix typo
## 1.11.0
### Added
- add the option for opening apps in new tab (fix #29)
### Fixed
- fix issue with the header of the always displayed menu
## 1.10.0
### Added
- add images to select the display of the menu
- add live preview
- update translations
- update app info
## 1.9.3
### Fixed
- fix regression with logo display
## 1.9.2
### Added
- add the option “always displayed” which fixes the position of the menu to the left and always displays the application icons (fix #21, fix #2)
## 1.8.6
### Added
- add translations: `fr` and `de`
- improve and publish the `Makefile`
- update documentation
## 1.8.5
### Fixed
- fix #28: menu items invisible after 1.8.4
## 1.8.4
### Fixed
- fix #27: disable side menu on public pages - broken in 1.8.3
## 1.8.3
### Fixed
- fix alphabetic order of apps (#26)
## 1.8.2
### Added
- add icon for closing the big menu (fix #25)
### Fixed
- fix hidden icons in the top menu (fix #23)
- fix missing apps (fix #24)
## 1.8.1
### Fixed
- fix issue with links
- fix missing l10n files
## 1.8.0
### Added
- add a `big menu` display (fix #22)
- add the possibility to choose what apps are displayed in the top menu (fix #22)
- add icon color filter
- add icon opacity filter
## 1.8.0-rc2
## 1.7.0
### Added
- add a loader when the page is unloading
- add compatibility with Nextcloud 19
## 1.6.3
### Fixed
- fix #20: add a shortcut to open and to hide the menu
## 1.6.2
### Fixed
- fix #19: add a cache to limit flashes
## 1.6.1
### Fixed
- fix #19: add a hack to show external sites in the top menu with navigation
## 1.6.0
### Added
- Add a page of personal settings
- Add an option to disable the side menu as user
- Refactoring of javascripts
## 1.5.0
### Added
- add option to force light icons instead of dark icons
### Fixed
- fix #19: add option to keep external sites in the top menu
- fix #16 #17: add dark icons and handle the svg filters
## 1.4.1
### Added
- Rendering harmonization with browsers
### Fixed
- FIX #15: make menu start after icon
## 1.4.0
### Fixed
- Fix #12: add an option to hide the opener and the panel when there is no application
## 1.3.4
### Fixed
- FIX #14: add option to show only the opener
- FIX #13: add alternate hamburger icon
- FIX #11: remove error in the console
## 1.3.3
### Added
- Add a second background color to create a background gradiant
- Update documentation
### Fixed
- FIX #10: add options to select the size of the icons and the text, or hide them
## 1.3.2
### Fixed
- FIX #9: opener icon not visible with multiple apps_paths
## 1.3.1
### Fixed
- FIX #3: Add an opition the choose the position of the opener (after or before the logo)
- FIX #8: Remove extension of dynamic asset's routes (js, css)
## 1.3.0
### Added
- `main.js` is replaced by a controller and a template
### Fixed
- FIX #2: add option to open the menu by hovering over opener
## 1.2.4
### Fixed
- FIX #7: Opener icon not visible in Safari
+5
View File
@@ -0,0 +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.
For more information please visit the [No Code of Conduct](https://github.com/domgetter/NCoC) homepage.
+8 -4
View File
@@ -11,10 +11,14 @@ release: npm-build translations
exit 1 exit 1
fi fi
test -d releases/$$VERSION && rm -fr releases/$$VERSION if [ -z "$$RELEASE_DIRECTORY" ]; then
mkdir -p releases/$$VERSION/side_menu RELEASE_DIRECTORY=releases
cp -r README.md appinfo css lib img l10n js src templates screenshots releases/$$VERSION/side_menu fi
cd releases/$$VERSION
test -d $$RELEASE_DIRECTORY/$$VERSION && rm -fr $$RELEASE_DIRECTORY/$$VERSION
mkdir -p $$RELEASE_DIRECTORY/$$VERSION/side_menu
cp -r README.md CHANGELOG.md appinfo css lib img l10n js src templates screenshots vendor $$RELEASE_DIRECTORY/$$VERSION/side_menu
cd $$RELEASE_DIRECTORY/$$VERSION
zip -r side_menu_v$$VERSION.zip side_menu zip -r side_menu_v$$VERSION.zip side_menu
tar cvzf side_menu_v$$VERSION.tar.gz side_menu tar cvzf side_menu_v$$VERSION.tar.gz side_menu
rm -fr side_menu rm -fr side_menu
+11 -3
View File
@@ -2,22 +2,25 @@
=============================== ===============================
Allows you to modify the position of the main menu by creating a panel on the left of the interface or with a big menu on the top. Allows you to modify the position of the main menu by creating a panel on the left of the interface or with a big menu on the top.
You can also define apps that must be displayed in the top menu. Fully customisable. You can also add and sort custom categories, define apps that must be displayed in the top menu, etc. Fully customisable.
This application is rather suitable for instances that activate a lot of applications. This application is rather suitable for instances that activate a lot of applications.
Comptatible with AppOrder. You can customize colors depending of the theme (Dark theme and Breeze Dark). Comptatible with AppOrder.
* [Installation and upgrade](#installation-and-upgrade) * [Installation and upgrade](#installation-and-upgrade)
* [How to contribute?](#how-to-contribute) * [How to contribute?](#how-to-contribute)
* [Support](#support)
* [Screenshots](https://gitnet.fr/deblan/side_menu/src/branch/master/screenshots/) * [Screenshots](https://gitnet.fr/deblan/side_menu/src/branch/master/screenshots/)
You like this app and you want to support me? ☕ [Buy me a coffee](https://www.buymeacoffee.com/deblan) or [Donate with liberapay](https://liberapay.com/deblan) You like this app and you want to support me? ☕ [Buy me a coffee](https://www.buymeacoffee.com/deblan) or [Donate with liberapay](https://liberapay.com/deblan)
[![Build Status](https://ci.gitnet.fr/api/badges/deblan/side_menu/status.svg)](https://ci.gitnet.fr/deblan/side_menu)
Requirements Requirements
------------ ------------
* PHP >= 7.3 * PHP >= 7.4
* App `theming` enabled * App `theming` enabled
Installation and upgrade Installation and upgrade
@@ -58,3 +61,8 @@ If you are a developer:
Build javascripts using `make npm-build` (or `make npm-watch` to build them in real time). Build javascripts using `make npm-build` (or `make npm-watch` to build them in real time).
Then commit and create a pull request. Then commit and create a pull request.
Support
-------
You can join the official room on Matrix: [#custommenu:neutralnetwork.org](https://matrix.to/#/#custommenu:neutralnetwork.org).
-10
View File
@@ -1,10 +0,0 @@
<?php
use OCA\SideMenu\AppInfo\Application;
$app = new Application();
if ($app->isEnabled()) {
$app->registerAssets();
$app->registerServices();
}
+6 -5
View File
@@ -11,13 +11,13 @@ 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. Use the shortcut `Ctrl`+`o` to open and to hide the side menu. Use `tab` to navigate.
Comptatible with AppOrder. You can customize colors depending of the theme (Dark theme and Breeze Dark). Comptatible with AppOrder.
You can report a bug or request a feature by opening an issue. You can report a bug or request a feature by opening an issue.
Requirements: Requirements:
* PHP >= 7.3 * PHP >= 7.4
* App `theming` enabled * App `theming` enabled
If you like this application and if you want to support the development: If you like this application and if you want to support the development:
@@ -26,7 +26,7 @@ If you like this application and if you want to support the development:
* [Donate with liberapay](https://liberapay.com/deblan) * [Donate with liberapay](https://liberapay.com/deblan)
* [Leave a comment](https://apps.nextcloud.com/apps/side_menu#comments) * [Leave a comment](https://apps.nextcloud.com/apps/side_menu#comments)
]]></description> ]]></description>
<version>1.21.0</version> <version>2.4.2</version>
<licence>agpl</licence> <licence>agpl</licence>
<author mail="contact@deblan.fr" homepage="https://www.deblan.io/">Simon Vieille</author> <author mail="contact@deblan.fr" homepage="https://www.deblan.io/">Simon Vieille</author>
<namespace>SideMenu</namespace> <namespace>SideMenu</namespace>
@@ -36,7 +36,7 @@ If you like this application and if you want to support the development:
</documentation> </documentation>
<category>customization</category> <category>customization</category>
<website>https://gitnet.fr/deblan/side_menu</website> <website>https://gitnet.fr/deblan/side_menu</website>
<discussion>https://matrix.to/#/!GhfQePnOSPYpYEunWM:neutralnetwork.org</discussion> <discussion>https://matrix.to/#/!TFPucDATKODpHNVAtu:neutralnetwork.org?via=neutralnetwork.org</discussion>
<bugs>https://gitnet.fr/deblan/side_menu/issues</bugs> <bugs>https://gitnet.fr/deblan/side_menu/issues</bugs>
<repository type="git">https://gitnet.fr/deblan/side_menu</repository> <repository type="git">https://gitnet.fr/deblan/side_menu</repository>
<screenshot>https://gitnet.fr/deblan/side_menu/raw/branch/master/screenshots/nc19_default_menu.png</screenshot> <screenshot>https://gitnet.fr/deblan/side_menu/raw/branch/master/screenshots/nc19_default_menu.png</screenshot>
@@ -46,7 +46,8 @@ If you like this application and if you want to support the development:
<screenshot>https://gitnet.fr/deblan/side_menu/raw/branch/master/screenshots/nc20_big_menu_responsive.png</screenshot> <screenshot>https://gitnet.fr/deblan/side_menu/raw/branch/master/screenshots/nc20_big_menu_responsive.png</screenshot>
<screenshot>https://gitnet.fr/deblan/side_menu/raw/branch/master/screenshots/personal_settings.png</screenshot> <screenshot>https://gitnet.fr/deblan/side_menu/raw/branch/master/screenshots/personal_settings.png</screenshot>
<dependencies> <dependencies>
<nextcloud min-version="18" max-version="20"/> <nextcloud min-version="20" max-version="24"/>
<php min-version="7.4"/>
</dependencies> </dependencies>
<settings> <settings>
<admin>OCA\SideMenu\Settings\Admin</admin> <admin>OCA\SideMenu\Settings\Admin</admin>
+77 -1
View File
@@ -20,7 +20,7 @@
margin: 10px 0 10px 0; margin: 10px 0 10px 0;
} }
#side-menu-section input[type="checkbox"] { #-dropside-menu-section input[type="checkbox"] {
vertical-align: middle; vertical-align: middle;
} }
@@ -68,3 +68,79 @@
.side-menu-toggler { .side-menu-toggler {
cursor: pointer; cursor: pointer;
} }
.side-menu-setting-list {
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;
}
.side-menu-setting-list-drop {
background: yellow;
border-color: yellow;
height: 34px;
}
.side-menu-setting.arrow {
color: #ccc;
padding-right: 5px;
}
.side-menu-setting-list-item input {
min-height: auto;
margin-top: -1px;
}
#apps-categories-custom-list select {
width: 100%;
}
.side-menu-setting-table {
display: table;
width: 100%;
}
.side-menu-setting-row {
display: table;
}
.side-menu-setting-label {
display: table-cell;
width: 400px;
padding-right: 20px;
}
.side-menu-setting-form {
display: table-cell;
min-width: 300px;
}
.side-menu-setting-label-short {
width: 300px;
}
.side-menu-setting-form-long {
width: 400px;
}
#side-menu-save-progress {
display: inline-block;
width: 0;
height: 15px;
background: #fff;
}
.btn-reset {
display: inline-block;
cursor: pointer;
position: absolute;
margin-top: 17px;
margin-left: 5px;
}
+56 -11
View File
@@ -29,12 +29,16 @@
display: none; display: none;
} }
#side-menu a {
transition: 0.2s;
}
#side-menu.open { #side-menu.open {
display: block; display: block;
} }
#header .side-menu-opener { #header .side-menu-opener {
margin-left: 5px; margin-left: 0px;
} }
.side-menu-settings { .side-menu-settings {
@@ -59,6 +63,8 @@
.side-menu-settings img { .side-menu-settings img {
vertical-align: bottom; vertical-align: bottom;
margin-left: 3px; margin-left: 3px;
width: 32px;
height: 32px;
} }
#side-menu.open .side-menu-settings { #side-menu.open .side-menu-settings {
@@ -67,10 +73,19 @@
.side-menu-opener { .side-menu-opener {
background: var(--side-menu-opener, url('../img/side-menu-opener.svg')); background: var(--side-menu-opener, url('../img/side-menu-opener.svg'));
height: 40px; background-color: transparent !important;
width: 40px; height: 40px !important;
border-radius: 0; width: 40px !important;
border: 0; 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;
} }
.side-menu-closer { .side-menu-closer {
@@ -105,6 +120,10 @@
margin-top: -3px; margin-top: -3px;
} }
.side-menu-app-icon .app-icon-notification {
display: none;
}
.side-menu-app a { .side-menu-app a {
line-height: 30px; line-height: 30px;
color: var(--side-menu-text-color, #fff); color: var(--side-menu-text-color, #fff);
@@ -135,10 +154,13 @@
max-width: 250px; max-width: 250px;
position: fixed; position: fixed;
padding-top: 2px; padding-top: 2px;
padding-left: 5px;
top: 0; top: 0;
} }
#side-menu.side-menu-with-categories .side-menu-header {
max-width: 295px;
}
#side-menu.hide-opener .side-menu-logo { #side-menu.hide-opener .side-menu-logo {
margin-top: 20px; margin-top: 20px;
} }
@@ -158,23 +180,23 @@
transition-property: width; transition-property: width;
} }
#side-menu.side-menu-big { #side-menu.side-menu-big, #side-menu.side-menu-with-categories {
max-width: 100%; max-width: 100%;
height: auto; height: auto;
} }
.side-menu-big .side-menu-header { .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-big .side-menu-apps-list, .side-menu-with-categories .side-menu-apps-list {
height: auto; height: auto;
position: static; position: static;
max-width: 100vw; max-width: 100vw;
overflow: auto; overflow: auto;
} }
.side-menu-big .side-menu-app a { .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;
} }
@@ -213,7 +235,7 @@
stroke: var(--side-menu-text-color, #fff); stroke: var(--side-menu-text-color, #fff);
} }
.side-menu-big .side-menu-app-icon { .side-menu-with-categories .side-menu-app-icon, .side-menu-big .side-menu-app-icon {
vertical-align: middle; vertical-align: middle;
margin-top: -2px; margin-top: -2px;
} }
@@ -235,6 +257,11 @@
.side-menu-always-displayed .side-menu-apps-list { .side-menu-always-displayed .side-menu-apps-list {
height: calc(100vh - 49px); height: calc(100vh - 49px);
top: 49px; top: 49px;
overflow: hidden;
}
.side-menu-always-displayed .side-menu-apps-list:hover {
overflow: auto;
} }
.side-menu-always-displayed #side-menu, .side-menu-always-displayed #side-menu,
@@ -267,6 +294,24 @@
transform: translateX(calc(-100% + 50px)) !important; transform: translateX(calc(-100% + 50px)) !important;
} }
#side-menu.side-menu-with-categories {
max-width: 290px;
height: 100vh;
}
.side-menu-with-categories .side-menu-categories {
display: block;
padding: 0;
}
.side-menu-with-categories .side-menu-category {
padding: 10px 0;
}
.side-menu-always-displayed #body-settings, #body-settings.body-settings-side-menu {
overflow-x: visible;
}
@media screen and (max-width: 1024px) { @media screen and (max-width: 1024px) {
#side-menu.side-menu-big { #side-menu.side-menu-big {
max-width: 290px; max-width: 290px;
@@ -0,0 +1,224 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="571.907"
height="156.921"
viewBox="0 0 151.317 41.519"
version="1.1"
id="svg901"
sodipodi:docname="layout-side-menu-with-categories.svg"
inkscape:version="0.92.4 (5da689c313, 2019-01-14)">
<metadata
id="metadata907">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<defs
id="defs905" />
<sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1918"
inkscape:window-height="1036"
id="namedview903"
showgrid="false"
inkscape:zoom="1.6086532"
inkscape:cx="455.61054"
inkscape:cy="16.694081"
inkscape:window-x="0"
inkscape:window-y="21"
inkscape:window-maximized="0"
inkscape:current-layer="svg901"
showguides="false" />
<path
style="marker:none"
color="#000"
overflow="visible"
fill="#fff"
stroke="#b3b3b3"
stroke-width=".359"
stroke-linecap="square"
paint-order="fill markers stroke"
d="M.18.18h58.532v41.16H.18zM62.735.18h58.532v41.16H62.735z"
id="path853" />
<g
fill="#b3b3b3"
stroke="#ccc"
fill-rule="evenodd"
stroke-width="1.465"
stroke-linecap="round"
id="g857">
<path
d="M84.066 21.35l5.055-5.056M82.925 29.835l18.152-18.151M95.893 25.225l3.585-3.586"
id="path855" />
</g>
<path
style="marker:none"
color="#000"
overflow="visible"
fill="none"
stroke="#b3b3b3"
stroke-width=".359"
stroke-linecap="square"
paint-order="fill markers stroke"
d="M62.735.18h58.532v41.16H62.735z"
id="path861" />
<path
style="marker:none"
color="#000"
overflow="visible"
fill="#f9f9f9"
stroke="#ccc"
stroke-width=".359"
stroke-linecap="square"
paint-order="fill markers stroke"
d="M.18.18h58.532v5.063H.18z"
id="path877" />
<path
style="marker:none"
color="#000"
overflow="visible"
fill="none"
stroke="#b3b3b3"
stroke-width=".359"
stroke-linecap="square"
paint-order="fill markers stroke"
d="M.18.18h58.532v41.16H.18z"
id="path879" />
<path
style="marker:none"
color="#000"
overflow="visible"
fill="#666"
stroke="#ccc"
stroke-width=".359"
stroke-linecap="square"
paint-order="fill markers stroke"
d="M1.357 1.034h3.496V4.39H1.357z"
id="path881" />
<path
style="marker:none"
color="#000"
overflow="visible"
fill="#e6e6e6"
stroke="#ccc"
stroke-width=".359"
stroke-linecap="square"
paint-order="fill markers stroke"
d="M6.068 1.034h6.102V4.39H6.068z"
id="path883" />
<g
fill="#b3b3b3"
stroke="#ccc"
fill-rule="evenodd"
stroke-width="1.465"
stroke-linecap="round"
id="g887">
<path
d="M21.51 21.35l5.056-5.056M20.37 29.835L38.52 11.684M33.337 25.225l3.586-3.586"
id="path885" />
</g>
<path
style="marker:none"
color="#000"
overflow="visible"
fill="#fff"
stroke="#b3b3b3"
stroke-width=".359"
stroke-linecap="square"
paint-order="fill markers stroke"
d="M125.857.18h25.28v41.16h-25.28z"
id="path889" />
<path
style="marker:none"
color="#000"
overflow="visible"
fill="#f9f9f9"
stroke="#ccc"
stroke-width=".359"
stroke-linecap="square"
paint-order="fill markers stroke"
d="M125.857.18h25.257v5.063h-25.257z"
id="path891" />
<path
style="marker:none"
color="#000"
overflow="visible"
fill="none"
stroke="#b3b3b3"
stroke-width=".359"
stroke-linecap="square"
paint-order="fill markers stroke"
d="M125.857.18h25.257v41.16h-25.257z"
id="path893" />
<path
style="marker:none"
color="#000"
overflow="visible"
fill="#4d4d4d"
stroke="#b3b3b3"
stroke-width=".359"
stroke-linecap="square"
paint-order="fill markers stroke"
d="M125.857.18h20.829v41.16h-20.83z"
id="path895" />
<path
style="marker:none"
color="#000"
overflow="visible"
fill="#666"
stroke="#ccc"
stroke-width=".359"
stroke-linecap="square"
paint-order="fill markers stroke"
d="M127.034 1.034h3.496V4.39h-3.496z"
id="path897" />
<path
style="marker:none"
color="#000"
overflow="visible"
fill="#e6e6e6"
stroke="#ccc"
stroke-width=".359"
stroke-linecap="square"
paint-order="fill markers stroke"
d="M127.569 10.155h1.893v1.81h-1.893zM130.498 10.486h12.463v1.148h-12.463zM127.569 13.696h1.893v1.81h-1.893zM130.498 14.027h10.592v1.148h-10.592zM127.569 17.104h1.893v1.81h-1.893zM130.498 17.435h12.997v1.148h-12.997zM127.569 24.708h1.893v1.81h-1.893zM130.498 25.04h7.585v1.147h-7.585zM127.569 28.116h1.893v1.81h-1.893zM130.498 28.447h11.46v1.148h-11.46zM127.569 31.657h1.893v1.81h-1.893zM130.498 31.988h9.322v1.148h-9.322zM127.569 35.065h1.893v1.81h-1.893zM130.498 35.396h11.126v1.148h-11.126zM127.616 6.848h11.518v1.148h-11.518zM127.569 21.311h9.344v1.148h-9.344z"
id="path899" />
<path
inkscape:connector-curvature="0"
style="color:#000000;overflow:visible;fill:#4d4d4d;stroke:#b3b3b3;stroke-width:0.359;stroke-linecap:square;marker:none;paint-order:fill markers stroke"
overflow="visible"
d="m 62.728587,0.17999881 h 20.829 V 41.34 h -20.83 z"
id="path895-5" />
<path
inkscape:connector-curvature="0"
style="color:#000000;overflow:visible;fill:#666666;stroke:#cccccc;stroke-width:0.359;stroke-linecap:square;marker:none;paint-order:fill markers stroke"
overflow="visible"
d="m 63.905587,1.0339988 h 3.496 V 4.389999 h -3.496 z"
id="path897-3" />
<path
inkscape:connector-curvature="0"
style="color:#000000;overflow:visible;fill:#e6e6e6;stroke:#cccccc;stroke-width:0.359;stroke-linecap:square;marker:none;paint-order:fill markers stroke"
overflow="visible"
d="m 64.440587,10.154999 h 1.893 v 1.81 h -1.893 z m 2.929,0.331 h 12.463 v 1.148 h -12.463 z m -2.929,3.21 h 1.893 v 1.81 h -1.893 z m 2.929,0.331 h 10.592 v 1.148 h -10.592 z m -2.929,3.077 h 1.893 v 1.81 h -1.893 z m 2.929,0.331 h 12.997 v 1.148 h -12.997 z m -2.929,7.273 h 1.893 V 26.518 h -1.893 z m 2.929,0.332 h 7.585 v 1.147 h -7.585 z m -2.929,3.076001 h 1.893 v 1.81 h -1.893 z m 2.929,0.330999 h 11.46 v 1.147999 h -11.46 z m -2.929,3.21 h 1.893 v 1.809999 h -1.893 z m 2.929,0.330999 h 9.322 v 1.148001 h -9.322 z m -2.929,3.077001 h 1.893 v 1.809999 h -1.893 z m 2.929,0.331001 h 11.126 v 1.147999 h -11.126 z m -2.882,-28.5480012 h 11.518 v 1.1480001 h -11.518 z m -0.047,14.4630002 h 9.344 v 1.148 h -9.344 z"
id="path899-5" />
</svg>

After

Width:  |  Height:  |  Size: 7.3 KiB

+223
View File
@@ -0,0 +1,223 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="571.907"
height="156.921"
viewBox="0 0 151.317 41.519"
version="1.1"
id="svg901"
sodipodi:docname="layout-side-menu-with-categories.svg"
inkscape:version="0.92.4 (5da689c313, 2019-01-14)">
<metadata
id="metadata907">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<defs
id="defs905" />
<sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1918"
inkscape:window-height="1036"
id="namedview903"
showgrid="false"
inkscape:zoom="1.6086532"
inkscape:cx="455.61054"
inkscape:cy="16.694081"
inkscape:window-x="0"
inkscape:window-y="21"
inkscape:window-maximized="0"
inkscape:current-layer="svg901" />
<path
style="marker:none"
color="#000"
overflow="visible"
fill="#fff"
stroke="#b3b3b3"
stroke-width=".359"
stroke-linecap="square"
paint-order="fill markers stroke"
d="M.18.18h58.532v41.16H.18zM62.735.18h58.532v41.16H62.735z"
id="path853" />
<g
fill="#b3b3b3"
stroke="#ccc"
fill-rule="evenodd"
stroke-width="1.465"
stroke-linecap="round"
id="g857">
<path
d="M84.066 21.35l5.055-5.056M82.925 29.835l18.152-18.151M95.893 25.225l3.585-3.586"
id="path855" />
</g>
<path
style="marker:none"
color="#000"
overflow="visible"
fill="none"
stroke="#b3b3b3"
stroke-width=".359"
stroke-linecap="square"
paint-order="fill markers stroke"
d="M62.735.18h58.532v41.16H62.735z"
id="path861" />
<path
style="marker:none"
color="#000"
overflow="visible"
fill="#f9f9f9"
stroke="#ccc"
stroke-width=".359"
stroke-linecap="square"
paint-order="fill markers stroke"
d="M.18.18h58.532v5.063H.18z"
id="path877" />
<path
style="marker:none"
color="#000"
overflow="visible"
fill="none"
stroke="#b3b3b3"
stroke-width=".359"
stroke-linecap="square"
paint-order="fill markers stroke"
d="M.18.18h58.532v41.16H.18z"
id="path879" />
<path
style="marker:none"
color="#000"
overflow="visible"
fill="#666"
stroke="#ccc"
stroke-width=".359"
stroke-linecap="square"
paint-order="fill markers stroke"
d="M1.357 1.034h3.496V4.39H1.357z"
id="path881" />
<path
style="marker:none"
color="#000"
overflow="visible"
fill="#e6e6e6"
stroke="#ccc"
stroke-width=".359"
stroke-linecap="square"
paint-order="fill markers stroke"
d="M6.068 1.034h6.102V4.39H6.068z"
id="path883" />
<g
fill="#b3b3b3"
stroke="#ccc"
fill-rule="evenodd"
stroke-width="1.465"
stroke-linecap="round"
id="g887">
<path
d="M21.51 21.35l5.056-5.056M20.37 29.835L38.52 11.684M33.337 25.225l3.586-3.586"
id="path885" />
</g>
<path
style="marker:none"
color="#000"
overflow="visible"
fill="#fff"
stroke="#b3b3b3"
stroke-width=".359"
stroke-linecap="square"
paint-order="fill markers stroke"
d="M125.857.18h25.28v41.16h-25.28z"
id="path889" />
<path
style="marker:none"
color="#000"
overflow="visible"
fill="#f9f9f9"
stroke="#ccc"
stroke-width=".359"
stroke-linecap="square"
paint-order="fill markers stroke"
d="M125.857.18h25.257v5.063h-25.257z"
id="path891" />
<path
style="marker:none"
color="#000"
overflow="visible"
fill="none"
stroke="#b3b3b3"
stroke-width=".359"
stroke-linecap="square"
paint-order="fill markers stroke"
d="M125.857.18h25.257v41.16h-25.257z"
id="path893" />
<path
style="marker:none"
color="#000"
overflow="visible"
fill="#4d4d4d"
stroke="#b3b3b3"
stroke-width=".359"
stroke-linecap="square"
paint-order="fill markers stroke"
d="M125.857.18h20.829v41.16h-20.83z"
id="path895" />
<path
style="marker:none"
color="#000"
overflow="visible"
fill="#666"
stroke="#ccc"
stroke-width=".359"
stroke-linecap="square"
paint-order="fill markers stroke"
d="M127.034 1.034h3.496V4.39h-3.496z"
id="path897" />
<path
style="marker:none"
color="#000"
overflow="visible"
fill="#e6e6e6"
stroke="#ccc"
stroke-width=".359"
stroke-linecap="square"
paint-order="fill markers stroke"
d="M127.569 10.155h1.893v1.81h-1.893zM130.498 10.486h12.463v1.148h-12.463zM127.569 13.696h1.893v1.81h-1.893zM130.498 14.027h10.592v1.148h-10.592zM127.569 17.104h1.893v1.81h-1.893zM130.498 17.435h12.997v1.148h-12.997zM127.569 24.708h1.893v1.81h-1.893zM130.498 25.04h7.585v1.147h-7.585zM127.569 28.116h1.893v1.81h-1.893zM130.498 28.447h11.46v1.148h-11.46zM127.569 31.657h1.893v1.81h-1.893zM130.498 31.988h9.322v1.148h-9.322zM127.569 35.065h1.893v1.81h-1.893zM130.498 35.396h11.126v1.148h-11.126zM127.616 6.848h11.518v1.148h-11.518zM127.569 21.311h9.344v1.148h-9.344z"
id="path899" />
<path
inkscape:connector-curvature="0"
style="color:#000000;overflow:visible;fill:#4d4d4d;stroke:#b3b3b3;stroke-width:0.359;stroke-linecap:square;marker:none;paint-order:fill markers stroke"
overflow="visible"
d="m 62.728587,0.17999881 h 20.829 V 41.34 h -20.83 z"
id="path895-5" />
<path
inkscape:connector-curvature="0"
style="color:#000000;overflow:visible;fill:#666666;stroke:#cccccc;stroke-width:0.359;stroke-linecap:square;marker:none;paint-order:fill markers stroke"
overflow="visible"
d="m 63.905587,1.0339988 h 3.496 V 4.389999 h -3.496 z"
id="path897-3" />
<path
inkscape:connector-curvature="0"
style="color:#000000;overflow:visible;fill:#e6e6e6;stroke:#cccccc;stroke-width:0.359;stroke-linecap:square;marker:none;paint-order:fill markers stroke"
overflow="visible"
d="m 64.440587,10.154999 h 1.893 v 1.81 h -1.893 z m 2.929,0.331 h 12.463 v 1.148 h -12.463 z m -2.929,3.21 h 1.893 v 1.81 h -1.893 z m 2.929,0.331 h 10.592 v 1.148 h -10.592 z m -2.929,3.077 h 1.893 v 1.81 h -1.893 z m 2.929,0.331 h 12.997 v 1.148 h -12.997 z m -2.929,7.273 h 1.893 V 26.518 h -1.893 z m 2.929,0.332 h 7.585 v 1.147 h -7.585 z m -2.929,3.076001 h 1.893 v 1.81 h -1.893 z m 2.929,0.330999 h 11.46 v 1.147999 h -11.46 z m -2.929,3.21 h 1.893 v 1.809999 h -1.893 z m 2.929,0.330999 h 9.322 v 1.148001 h -9.322 z m -2.929,3.077001 h 1.893 v 1.809999 h -1.893 z m 2.929,0.331001 h 11.126 v 1.147999 h -11.126 z m -2.882,-28.5480012 h 11.518 v 1.1480001 h -11.518 z m -0.047,14.4630002 h 9.344 v 1.148 h -9.344 z"
id="path899-5" />
</svg>

After

Width:  |  Height:  |  Size: 7.3 KiB

+1 -113
View File
@@ -1,113 +1 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?> <svg xmlns="http://www.w3.org/2000/svg" width="145.889" height="145.889" viewBox="0 0 38.6 38.6"><g transform="translate(-59.172 -91.92)" stroke="#000" fill="none"><path d="M66.595 111.162h9.997M66.595 103.805h11.4m-11.4 14.714h11.4" stroke-width="3.165"/><rect width="34.535" height="34.535" x="61.205" y="93.952" overflow="visible" rx="5.481" ry="5.481" style="marker:none" color="#000" stroke-width="4.065" stroke-linecap="round" paint-order="fill markers stroke"/></g></svg>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="37.099998mm"
height="37.099998mm"
viewBox="0 0 37.099998 37.099998"
version="1.1"
id="svg8"
inkscape:version="0.92.4 (5da689c313, 2019-01-14)"
sodipodi:docname="icon.svg">
<defs
id="defs2" />
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="2.8"
inkscape:cx="242.1839"
inkscape:cy="32.026866"
inkscape:document-units="mm"
inkscape:current-layer="layer1"
showgrid="false"
fit-margin-top="0"
fit-margin-left="0"
fit-margin-right="0"
fit-margin-bottom="0"
inkscape:window-width="1918"
inkscape:window-height="1027"
inkscape:window-x="1920"
inkscape:window-y="21"
inkscape:window-maximized="0"
inkscape:snap-nodes="false" />
<metadata
id="metadata5">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Calque 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(-59.922223,-92.669069)">
<rect
style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:1;vector-effect:none;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:#1a1a1a;stroke-width:1.46500015;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;paint-order:fill markers stroke;enable-background:accumulate"
id="rect815-6-2-7"
width="34.535"
height="34.535"
x="61.204723"
y="93.951569"
rx="5.4806547"
ry="5.4806547" />
<rect
style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:1;vector-effect:none;fill:#ececec;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.46500015;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;paint-order:fill markers stroke;enable-background:accumulate"
id="rect815-6-7"
width="20.357224"
height="33.40107"
x="61.677193"
y="94.518532"
rx="0"
ry="0" />
<path
style="fill:none;fill-rule:evenodd;stroke:#333333;stroke-width:3.16499996;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 66.59458,111.16177 h 9.997359"
id="path873-5"
inkscape:connector-curvature="0" />
<g
id="g1018"
transform="translate(2.6458333)"
style="stroke:#333333;stroke-opacity:1">
<g
id="g1013"
style="stroke:#333333;stroke-opacity:1">
<path
style="fill:none;fill-rule:evenodd;stroke:#333333;stroke-width:3.16499996;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 63.948747,103.80467 H 75.349244"
id="path873"
inkscape:connector-curvature="0" />
<path
style="fill:none;fill-rule:evenodd;stroke:#333333;stroke-width:3.16499996;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 63.948747,118.51887 H 75.349244"
id="path873-5-3"
inkscape:connector-curvature="0" />
</g>
</g>
<rect
style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:1;vector-effect:none;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#333333;stroke-width:2.56500006;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;paint-order:fill markers stroke;enable-background:accumulate"
id="rect815-6-2"
width="34.535"
height="34.535"
x="61.204723"
y="93.951569"
rx="5.4806547"
ry="5.4806547" />
</g>
</svg>

Before

Width:  |  Height:  |  Size: 4.5 KiB

After

Width:  |  Height:  |  Size: 478 B

+1 -6
View File
@@ -1,6 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="40" height="40" viewBox="0 0 10.583 10.583"> <svg xmlns="http://www.w3.org/2000/svg" width="40" height="40" viewBox="0 0 10.583 10.583"><g fill="#fff" color="#000" transform="translate(-.067 .224)"><rect width="5.027" height=".777" x="-2.307" y="6.984" overflow="visible" paint-order="stroke markers fill" rx="0" ry="0" style="marker:none" transform="rotate(-45)"/><rect width="5.027" height=".777" x="4.859" y="-.595" overflow="visible" paint-order="stroke markers fill" rx="0" ry="0" style="marker:none" transform="rotate(45)"/></g></svg>
<g color="#000" fill="#fff">
<rect transform="rotate(135)" ry=".545" rx=".545" y="-8.209" x="-3.608" height="1.451" width="7.216" style="marker:none" overflow="visible" paint-order="stroke markers fill"/>
<rect transform="rotate(-135)" ry=".545" rx=".545" y="-.726" x="-11.091" height="1.451" width="7.216" style="marker:none" overflow="visible" paint-order="stroke markers fill"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 498 B

After

Width:  |  Height:  |  Size: 495 B

+1 -173
View File
@@ -1,173 +1 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?> <svg xmlns="http://www.w3.org/2000/svg" width="40" height="40" viewBox="0 0 10.583 10.583"><g fill-opacity=".898" color="#000" transform="matrix(.90559 0 0 .86896 .772 -247.893)"><rect width="1.451" height="1.451" x="4.266" y="288.474" overflow="visible" paint-order="stroke markers fill" rx="0" ry="0" style="marker:none"/><g transform="translate(-.13)"><rect width="1.451" height="1.451" x="6.445" y="288.474" overflow="visible" paint-order="stroke markers fill" rx="0" ry="0" style="marker:none"/><rect width="1.451" height="1.451" x="2.345" y="288.474" overflow="visible" paint-order="stroke markers fill" rx="0" ry="0" style="marker:none"/></g><rect width="1.451" height="1.451" x="4.266" y="290.631" overflow="visible" paint-order="stroke markers fill" rx="0" ry="0" style="marker:none"/><g transform="translate(-.13 2.157)"><rect width="1.451" height="1.451" x="6.445" y="288.474" overflow="visible" paint-order="stroke markers fill" rx="0" ry="0" style="marker:none"/><rect width="1.451" height="1.451" x="2.345" y="288.474" overflow="visible" paint-order="stroke markers fill" rx="0" ry="0" style="marker:none"/></g><rect width="1.451" height="1.451" x="4.266" y="292.808" overflow="visible" paint-order="stroke markers fill" rx="0" ry="0" style="marker:none"/><g transform="translate(-.13 4.334)"><rect width="1.451" height="1.451" x="6.445" y="288.474" overflow="visible" paint-order="stroke markers fill" rx="0" ry="0" style="marker:none"/><rect width="1.451" height="1.451" x="2.345" y="288.474" overflow="visible" paint-order="stroke markers fill" rx="0" ry="0" style="marker:none"/></g></g></svg>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="40"
height="40"
viewBox="0 0 10.583 10.583"
version="1.1"
id="svg935"
sodipodi:docname="side-menu-opener-dark.svg"
inkscape:version="0.92.4 (5da689c313, 2019-01-14)">
<metadata
id="metadata941">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<defs
id="defs939" />
<sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1918"
inkscape:window-height="1008"
id="namedview937"
showgrid="false"
inkscape:zoom="8.34386"
inkscape:cx="83.025045"
inkscape:cy="14.304895"
inkscape:window-x="0"
inkscape:window-y="41"
inkscape:window-maximized="0"
inkscape:current-layer="svg935" />
<g
transform="translate(.3 -286.074)"
fill="#fff"
color="#000"
fill-opacity=".855"
id="g933"
style="fill:#000000;fill-opacity:0.89855075">
<rect
ry="0"
rx="0"
y="288.474"
x="4.266"
height="1.451"
width="1.451"
style="marker:none;fill:#000000;fill-opacity:0.89855075"
overflow="visible"
paint-order="stroke markers fill"
id="rect909" />
<g
transform="translate(-.13)"
id="g915"
style="fill:#000000;fill-opacity:0.89855075">
<rect
style="marker:none;fill:#000000;fill-opacity:0.89855075"
width="1.451"
height="1.451"
x="6.445"
y="288.474"
rx="0"
ry="0"
overflow="visible"
paint-order="stroke markers fill"
id="rect911" />
<rect
style="marker:none;fill:#000000;fill-opacity:0.89855075"
width="1.451"
height="1.451"
x="2.345"
y="288.474"
rx="0"
ry="0"
overflow="visible"
paint-order="stroke markers fill"
id="rect913" />
</g>
<rect
ry="0"
rx="0"
y="290.631"
x="4.266"
height="1.451"
width="1.451"
style="marker:none;fill:#000000;fill-opacity:0.89855075"
overflow="visible"
paint-order="stroke markers fill"
id="rect917" />
<g
transform="translate(-.13 2.157)"
id="g923"
style="fill:#000000;fill-opacity:0.89855075">
<rect
style="marker:none;fill:#000000;fill-opacity:0.89855075"
width="1.451"
height="1.451"
x="6.445"
y="288.474"
rx="0"
ry="0"
overflow="visible"
paint-order="stroke markers fill"
id="rect919" />
<rect
style="marker:none;fill:#000000;fill-opacity:0.89855075"
width="1.451"
height="1.451"
x="2.345"
y="288.474"
rx="0"
ry="0"
overflow="visible"
paint-order="stroke markers fill"
id="rect921" />
</g>
<rect
ry="0"
rx="0"
y="292.808"
x="4.266"
height="1.451"
width="1.451"
style="marker:none;fill:#000000;fill-opacity:0.89855075"
overflow="visible"
paint-order="stroke markers fill"
id="rect925" />
<g
transform="translate(-.13 4.334)"
id="g931"
style="fill:#000000;fill-opacity:0.89855075">
<rect
style="marker:none;fill:#000000;fill-opacity:0.89855075"
width="1.451"
height="1.451"
x="6.445"
y="288.474"
rx="0"
ry="0"
overflow="visible"
paint-order="stroke markers fill"
id="rect927" />
<rect
style="marker:none;fill:#000000;fill-opacity:0.89855075"
width="1.451"
height="1.451"
x="2.345"
y="288.474"
rx="0"
ry="0"
overflow="visible"
paint-order="stroke markers fill"
id="rect929" />
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 4.5 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

+1 -102
View File
@@ -1,102 +1 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?> <svg xmlns="http://www.w3.org/2000/svg" width="40" height="40" viewBox="0 0 10.583 10.583"><rect width="7.62" height="7.62" x="1.482" y="1.482" fill="none" stroke="#000" stroke-linecap="square" stroke-width=".777" color="#000" overflow="visible" paint-order="markers fill stroke" rx="0" ry="0" style="marker:none"/><rect width="5.027" height=".777" x="2.778" y="3.148" color="#000" overflow="visible" paint-order="stroke markers fill" rx="0" ry="0" style="marker:none"/><rect width="5.027" height=".777" x="2.778" y="6.658" color="#000" overflow="visible" paint-order="stroke markers fill" rx="0" ry="0" style="marker:none"/><rect width="5.027" height=".777" x="2.778" y="4.936" color="#000" overflow="visible" paint-order="stroke markers fill" rx="0" ry="0" style="marker:none"/></svg>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="40"
height="40"
viewBox="0 0 10.583 10.583"
version="1.1"
id="svg1090"
sodipodi:docname="side-menu-opener-hamburger-2-dark.svg"
inkscape:version="0.92.4 (5da689c313, 2019-01-14)">
<metadata
id="metadata1096">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<defs
id="defs1094" />
<sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1918"
inkscape:window-height="1008"
id="namedview1092"
showgrid="false"
inkscape:zoom="8.34386"
inkscape:cx="84.523455"
inkscape:cy="12.270318"
inkscape:window-x="0"
inkscape:window-y="41"
inkscape:window-maximized="0"
inkscape:current-layer="svg1090" />
<rect
overflow="visible"
style="marker:none;fill:#000000;fill-opacity:1"
width="4.806"
height="1.256"
x="2.888"
y="2.787"
rx="0"
ry=".472"
color="#000"
fill="#fff"
fill-opacity=".855"
paint-order="stroke markers fill"
id="rect1082" />
<rect
overflow="visible"
style="marker:none;fill:#000000;fill-opacity:1"
width="4.806"
height="1.256"
x="2.888"
y="4.655"
rx="0"
ry=".472"
color="#000"
fill="#fff"
fill-opacity=".855"
paint-order="stroke markers fill"
id="rect1084" />
<rect
overflow="visible"
style="marker:none;fill:#000000;fill-opacity:1"
width="4.806"
height="1.256"
x="2.888"
y="6.54"
rx="0"
ry=".472"
color="#000"
fill="#fff"
fill-opacity=".855"
paint-order="stroke markers fill"
id="rect1086" />
<path
style="marker:none;fill:#000000;fill-opacity:1"
d="M1.243.71A.469.469 0 0 0 .881.88a.469.469 0 0 0-.171.362V9.34c0 .146.067.275.171.362a.469.469 0 0 0 .362.171H9.34a.469.469 0 0 0 .362-.171.469.469 0 0 0 .171-.362V1.243a.469.469 0 0 0-.171-.362A.469.469 0 0 0 9.34.71zm.723 1.256h6.65v6.65h-6.65z"
color="#000"
overflow="visible"
fill="#fff"
fill-opacity=".855"
paint-order="stroke markers fill"
id="path1088" />
</svg>

Before

Width:  |  Height:  |  Size: 2.8 KiB

After

Width:  |  Height:  |  Size: 786 B

+1 -101
View File
@@ -1,101 +1 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?> <svg xmlns="http://www.w3.org/2000/svg" width="40" height="40" viewBox="0 0 10.583 10.583"><rect width="7.62" height="7.62" x="1.482" y="1.482" fill="none" stroke="#fff" stroke-linecap="square" stroke-width=".777" color="#000" overflow="visible" paint-order="markers fill stroke" rx="0" ry="0" style="marker:none"/><g fill="#fff" color="#000" transform="translate(0 .034)"><rect width="5.027" height=".777" x="2.778" y="3.114" overflow="visible" paint-order="stroke markers fill" rx="0" ry="0" style="marker:none"/><rect width="5.027" height=".777" x="2.778" y="6.624" overflow="visible" paint-order="stroke markers fill" rx="0" ry="0" style="marker:none"/><rect width="5.027" height=".777" x="2.778" y="4.903" overflow="visible" paint-order="stroke markers fill" rx="0" ry="0" style="marker:none"/></g></svg>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="40"
height="40"
viewBox="0 0 10.583 10.583"
version="1.1"
id="svg1443"
sodipodi:docname="side-menu-opener-hamburger-2.svg"
inkscape:version="0.92.4 (5da689c313, 2019-01-14)">
<metadata
id="metadata1449">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
</cc:Work>
</rdf:RDF>
</metadata>
<defs
id="defs1447" />
<sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1918"
inkscape:window-height="1008"
id="namedview1445"
showgrid="false"
inkscape:zoom="5.9"
inkscape:cx="90.508475"
inkscape:cy="4.5762712"
inkscape:window-x="0"
inkscape:window-y="41"
inkscape:window-maximized="0"
inkscape:current-layer="svg1443" />
<rect
overflow="visible"
style="marker:none;fill:#ffffff;fill-opacity:1"
width="4.806"
height="1.256"
x="2.888"
y="2.787"
rx="0"
ry=".472"
color="#000"
fill="#fff"
fill-opacity=".855"
paint-order="stroke markers fill"
id="rect1435" />
<rect
overflow="visible"
style="marker:none;fill:#ffffff;fill-opacity:1"
width="4.806"
height="1.256"
x="2.888"
y="4.655"
rx="0"
ry=".472"
color="#000"
fill="#fff"
fill-opacity=".855"
paint-order="stroke markers fill"
id="rect1437" />
<rect
overflow="visible"
style="marker:none;fill:#ffffff;fill-opacity:1"
width="4.806"
height="1.256"
x="2.888"
y="6.54"
rx="0"
ry=".472"
color="#000"
fill="#fff"
fill-opacity=".855"
paint-order="stroke markers fill"
id="rect1439" />
<path
style="marker:none;fill:#ffffff;fill-opacity:1"
d="M1.243.71A.469.469 0 0 0 .881.88a.469.469 0 0 0-.171.362V9.34c0 .146.067.275.171.362a.469.469 0 0 0 .362.171H9.34a.469.469 0 0 0 .362-.171.469.469 0 0 0 .171-.362V1.243a.469.469 0 0 0-.171-.362A.469.469 0 0 0 9.34.71zm.723 1.256h6.65v6.65h-6.65z"
color="#000"
overflow="visible"
fill="#fff"
fill-opacity=".855"
paint-order="stroke markers fill"
id="path1441" />
</svg>

Before

Width:  |  Height:  |  Size: 2.8 KiB

After

Width:  |  Height:  |  Size: 809 B

+1 -92
View File
@@ -1,92 +1 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?> <svg xmlns="http://www.w3.org/2000/svg" width="40" height="40" viewBox="0 0 10.583 10.583"><g color="#000" transform="matrix(.85624 0 0 .9944 .747 .03)"><g transform="matrix(1.0055 0 0 1 -.013 0)"><rect width="5.839" height=".782" x="2.372" y="2.764" overflow="visible" paint-order="stroke markers fill" rx="0" ry="0" style="marker:none"/><rect width="5.839" height=".782" x="2.372" y="7.037" overflow="visible" paint-order="stroke markers fill" rx="0" ry="0" style="marker:none"/></g><rect width="5.871" height=".782" x="2.372" y="4.901" overflow="visible" paint-order="stroke markers fill" rx="0" ry="0" style="marker:none"/></g></svg>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="40"
height="40"
viewBox="0 0 10.583 10.583"
version="1.1"
id="svg1168"
sodipodi:docname="side-menu-opener-hamburger-dark.svg"
inkscape:version="0.92.4 (5da689c313, 2019-01-14)">
<metadata
id="metadata1174">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<defs
id="defs1172" />
<sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1918"
inkscape:window-height="1008"
id="namedview1170"
showgrid="false"
inkscape:zoom="5.9"
inkscape:cx="112.71186"
inkscape:cy="0.16949153"
inkscape:window-x="0"
inkscape:window-y="41"
inkscape:window-maximized="0"
inkscape:current-layer="svg1168" />
<g
color="#000"
fill="#fff"
fill-opacity=".855"
transform="translate(0 -286.417)"
id="g1166"
style="fill:#000000;fill-opacity:0.99637681">
<rect
ry=".545"
rx="0"
y="288.816"
x="2.516"
height="1.451"
width="5.551"
style="marker:none;fill:#000000;fill-opacity:0.99637681"
overflow="visible"
paint-order="stroke markers fill"
id="rect1160" />
<rect
ry=".545"
rx="0"
y="290.973"
x="2.516"
height="1.451"
width="5.551"
style="marker:none;fill:#000000;fill-opacity:0.99637681"
overflow="visible"
paint-order="stroke markers fill"
id="rect1162" />
<rect
ry=".545"
rx="0"
y="293.15"
x="2.516"
height="1.451"
width="5.551"
style="marker:none;fill:#000000;fill-opacity:0.99637681"
overflow="visible"
paint-order="stroke markers fill"
id="rect1164" />
</g>
</svg>

Before

Width:  |  Height:  |  Size: 2.5 KiB

After

Width:  |  Height:  |  Size: 637 B

+1 -91
View File
@@ -1,91 +1 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?> <svg xmlns="http://www.w3.org/2000/svg" width="40" height="40" viewBox="0 0 10.583 10.583"><g fill="#fff" color="#000" transform="matrix(.85624 0 0 .9944 .747 .03)"><g transform="matrix(1.0055 0 0 1 -.013 0)"><rect width="5.839" height=".782" x="2.372" y="2.764" overflow="visible" paint-order="stroke markers fill" rx="0" ry="0" style="marker:none"/><rect width="5.839" height=".782" x="2.372" y="7.037" overflow="visible" paint-order="stroke markers fill" rx="0" ry="0" style="marker:none"/></g><rect width="5.871" height=".782" x="2.372" y="4.901" overflow="visible" paint-order="stroke markers fill" rx="0" ry="0" style="marker:none"/></g></svg>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="40"
height="40"
viewBox="0 0 10.583 10.583"
version="1.1"
id="svg1382"
sodipodi:docname="side-menu-opener-hamburger.svg"
inkscape:version="0.92.4 (5da689c313, 2019-01-14)">
<metadata
id="metadata1388">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
</cc:Work>
</rdf:RDF>
</metadata>
<defs
id="defs1386" />
<sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1918"
inkscape:window-height="1008"
id="namedview1384"
showgrid="false"
inkscape:zoom="5.9"
inkscape:cx="108.13559"
inkscape:cy="3.8983051"
inkscape:window-x="0"
inkscape:window-y="41"
inkscape:window-maximized="0"
inkscape:current-layer="svg1382" />
<g
color="#000"
fill="#fff"
fill-opacity=".855"
transform="translate(0 -286.417)"
id="g1380"
style="fill:#ffffff;fill-opacity:1">
<rect
ry=".545"
rx="0"
y="288.816"
x="2.516"
height="1.451"
width="5.551"
style="marker:none;fill:#ffffff;fill-opacity:1"
overflow="visible"
paint-order="stroke markers fill"
id="rect1374" />
<rect
ry=".545"
rx="0"
y="290.973"
x="2.516"
height="1.451"
width="5.551"
style="marker:none;fill:#ffffff;fill-opacity:1"
overflow="visible"
paint-order="stroke markers fill"
id="rect1376" />
<rect
ry=".545"
rx="0"
y="293.15"
x="2.516"
height="1.451"
width="5.551"
style="marker:none;fill:#ffffff;fill-opacity:1"
overflow="visible"
paint-order="stroke markers fill"
id="rect1378" />
</g>
</svg>

Before

Width:  |  Height:  |  Size: 2.4 KiB

After

Width:  |  Height:  |  Size: 649 B

+1 -172
View File
@@ -1,172 +1 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?> <svg xmlns="http://www.w3.org/2000/svg" width="40" height="40" viewBox="0 0 10.583 10.583"><g fill="#fff" fill-opacity=".946" color="#000" transform="matrix(.90559 0 0 .86896 .772 -247.893)"><rect width="1.451" height="1.451" x="4.266" y="288.474" overflow="visible" paint-order="stroke markers fill" rx="0" ry="0" style="marker:none"/><g transform="translate(-.13)"><rect width="1.451" height="1.451" x="6.445" y="288.474" overflow="visible" paint-order="stroke markers fill" rx="0" ry="0" style="marker:none"/><rect width="1.451" height="1.451" x="2.345" y="288.474" overflow="visible" paint-order="stroke markers fill" rx="0" ry="0" style="marker:none"/></g><rect width="1.451" height="1.451" x="4.266" y="290.631" overflow="visible" paint-order="stroke markers fill" rx="0" ry="0" style="marker:none"/><g transform="translate(-.13 2.157)"><rect width="1.451" height="1.451" x="6.445" y="288.474" overflow="visible" paint-order="stroke markers fill" rx="0" ry="0" style="marker:none"/><rect width="1.451" height="1.451" x="2.345" y="288.474" overflow="visible" paint-order="stroke markers fill" rx="0" ry="0" style="marker:none"/></g><rect width="1.451" height="1.451" x="4.266" y="292.808" overflow="visible" paint-order="stroke markers fill" rx="0" ry="0" style="marker:none"/><g transform="translate(-.13 4.334)"><rect width="1.451" height="1.451" x="6.445" y="288.474" overflow="visible" paint-order="stroke markers fill" rx="0" ry="0" style="marker:none"/><rect width="1.451" height="1.451" x="2.345" y="288.474" overflow="visible" paint-order="stroke markers fill" rx="0" ry="0" style="marker:none"/></g></g></svg>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="40"
height="40"
viewBox="0 0 10.583 10.583"
version="1.1"
id="svg1321"
sodipodi:docname="side-menu-opener.svg"
inkscape:version="0.92.4 (5da689c313, 2019-01-14)">
<metadata
id="metadata1327">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
</cc:Work>
</rdf:RDF>
</metadata>
<defs
id="defs1325" />
<sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1918"
inkscape:window-height="1008"
id="namedview1323"
showgrid="false"
inkscape:zoom="5.9"
inkscape:cx="78.983051"
inkscape:cy="5.5932203"
inkscape:window-x="0"
inkscape:window-y="41"
inkscape:window-maximized="0"
inkscape:current-layer="svg1321" />
<g
transform="translate(.3 -286.074)"
fill="#fff"
color="#000"
fill-opacity=".855"
id="g1319"
style="fill:#ffffff;fill-opacity:0.94565219">
<rect
ry="0"
rx="0"
y="288.474"
x="4.266"
height="1.451"
width="1.451"
style="marker:none;fill:#ffffff;fill-opacity:0.94565219"
overflow="visible"
paint-order="stroke markers fill"
id="rect1295" />
<g
transform="translate(-.13)"
id="g1301"
style="fill:#ffffff;fill-opacity:0.94565219">
<rect
style="marker:none;fill:#ffffff;fill-opacity:0.94565219"
width="1.451"
height="1.451"
x="6.445"
y="288.474"
rx="0"
ry="0"
overflow="visible"
paint-order="stroke markers fill"
id="rect1297" />
<rect
style="marker:none;fill:#ffffff;fill-opacity:0.94565219"
width="1.451"
height="1.451"
x="2.345"
y="288.474"
rx="0"
ry="0"
overflow="visible"
paint-order="stroke markers fill"
id="rect1299" />
</g>
<rect
ry="0"
rx="0"
y="290.631"
x="4.266"
height="1.451"
width="1.451"
style="marker:none;fill:#ffffff;fill-opacity:0.94565219"
overflow="visible"
paint-order="stroke markers fill"
id="rect1303" />
<g
transform="translate(-.13 2.157)"
id="g1309"
style="fill:#ffffff;fill-opacity:0.94565219">
<rect
style="marker:none;fill:#ffffff;fill-opacity:0.94565219"
width="1.451"
height="1.451"
x="6.445"
y="288.474"
rx="0"
ry="0"
overflow="visible"
paint-order="stroke markers fill"
id="rect1305" />
<rect
style="marker:none;fill:#ffffff;fill-opacity:0.94565219"
width="1.451"
height="1.451"
x="2.345"
y="288.474"
rx="0"
ry="0"
overflow="visible"
paint-order="stroke markers fill"
id="rect1307" />
</g>
<rect
ry="0"
rx="0"
y="292.808"
x="4.266"
height="1.451"
width="1.451"
style="marker:none;fill:#ffffff;fill-opacity:0.94565219"
overflow="visible"
paint-order="stroke markers fill"
id="rect1311" />
<g
transform="translate(-.13 4.334)"
id="g1317"
style="fill:#ffffff;fill-opacity:0.94565219">
<rect
style="marker:none;fill:#ffffff;fill-opacity:0.94565219"
width="1.451"
height="1.451"
x="6.445"
y="288.474"
rx="0"
ry="0"
overflow="visible"
paint-order="stroke markers fill"
id="rect1313" />
<rect
style="marker:none;fill:#ffffff;fill-opacity:0.94565219"
width="1.451"
height="1.451"
x="2.345"
y="288.474"
rx="0"
ry="0"
overflow="visible"
paint-order="stroke markers fill"
id="rect1315" />
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 4.5 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

+60 -58
View File
@@ -6,8 +6,12 @@ use OC;
use OC\Security\CSP\ContentSecurityPolicyNonceManager; use OC\Security\CSP\ContentSecurityPolicyNonceManager;
use OC\User\User; use OC\User\User;
use OCA\SideMenu\Service\AppRepository; use OCA\SideMenu\Service\AppRepository;
use OCA\SideMenu\Service\CategoryRepository;
use OCA\SideMenu\Service\ConfigProxy; use OCA\SideMenu\Service\ConfigProxy;
use OCP\AppFramework\App; use OCP\AppFramework\App;
use OCP\AppFramework\Bootstrap\IBootContext;
use OCP\AppFramework\Bootstrap\IBootstrap;
use OCP\AppFramework\Bootstrap\IRegistrationContext;
use OCP\IUserSession; use OCP\IUserSession;
use OCP\Util; use OCP\Util;
use Psr\Container\ContainerInterface; use Psr\Container\ContainerInterface;
@@ -17,7 +21,7 @@ use Psr\Container\ContainerInterface;
* *
* @author Simon Vieille <simon@deblan.fr> * @author Simon Vieille <simon@deblan.fr>
*/ */
class Application extends App class Application extends App implements IBootstrap
{ {
public const APP_ID = 'side_menu'; public const APP_ID = 'side_menu';
@@ -43,16 +47,9 @@ class Application extends App
public function __construct(array $urlParams = []) public function __construct(array $urlParams = [])
{ {
parent::__construct(self::APP_ID, $urlParams); parent::__construct(self::APP_ID, $urlParams);
$this->config = OC::$server->getConfig();
$this->cspnm = OC::$server->getContentSecurityPolicyNonceManager();
$this->user = OC::$server[IUserSession::class]->getUser();
} }
/** protected function isEnabled(): bool
* Checks if this app is enabled.
*/
public function isEnabled(): bool
{ {
$enabled = true; $enabled = true;
$isForced = (bool) $this->config->getAppValue(self::APP_ID, 'force', '0'); $isForced = (bool) $this->config->getAppValue(self::APP_ID, 'force', '0');
@@ -73,60 +70,65 @@ class Application extends App
return $enabled; return $enabled;
} }
/** protected function addAssets()
* Registes services.
*/
public function registerServices()
{
$container = $this->getContainer();
$container->registerService('AppRepository', function (ContainerInterface $c) {
return new AppRepository();
});
$container->registerService('ConfigProxy', function (ContainerInterface $c) {
return new ConfigProxy($this->config);
});
}
/**
* Registers assets.
*/
public function registerAssets()
{ {
Util::addScript(self::APP_ID, 'sideMenu'); Util::addScript(self::APP_ID, 'sideMenu');
Util::addStyle(self::APP_ID, 'sideMenu'); Util::addStyle(self::APP_ID, 'sideMenu');
$stylesheet = OC::$server->getURLGenerator()->linkToRoute( $assets = [
'side_menu.Css.stylesheet', 'stylesheet' => [
[ 'route' => 'side_menu.Css.stylesheet',
'v' => $this->config->getAppValue(self::APP_ID, 'cache', '0'), 'type' => 'link',
] 'route_attr' => 'href',
); 'attr' => [
'rel' => 'stylesheet',
$script = OC::$server->getURLGenerator()->linkToRoute( ],
'side_menu.Js.script',
[
'v' => $this->config->getAppValue(self::APP_ID, 'cache', '0'),
]
);
Util::addHeader(
'link',
[
'href' => $stylesheet,
'rel' => 'stylesheet',
], ],
'' 'script' => [
); 'route' => 'side_menu.Js.script',
'type' => 'script',
Util::addHeader( 'route_attr' => 'src',
'script', 'attr' => [
[ 'nonce' => $this->cspnm->getNonce(),
'src' => $script, ],
'nonce' => $this->cspnm->getNonce(),
], ],
'' ];
);
$cache = $this->config->getAppValue(self::APP_ID, 'cache', '0');
foreach ($assets as $key => $value) {
$route = OC::$server->getURLGenerator()->linkToRoute($value['route'], ['v' => $cache]);
$value['attr'][$value['route_attr']] = $route;
Util::addHeader($value['type'], $value['attr'], '');
}
}
public function register(IRegistrationContext $context): void
{
$context->registerService('AppRepository', function (ContainerInterface $c) {
return new AppRepository();
});
$context->registerService('CategoryRepository', function (ContainerInterface $c) {
return new CategoryRepository();
});
$context->registerService('ConfigProxy', function (ContainerInterface $c) {
return new ConfigProxy();
});
}
public function boot(IBootContext $context): void
{
$this->config = OC::$server->getConfig();
$this->cspnm = OC::$server->getContentSecurityPolicyNonceManager();
$this->user = OC::$server[IUserSession::class]->getUser();
if (!$this->isEnabled()) {
return;
}
$this->addAssets();
} }
} }
+81 -9
View File
@@ -27,6 +27,8 @@ use OCP\AppFramework\Http\Response;
use OCP\AppFramework\Http\TemplateResponse; use OCP\AppFramework\Http\TemplateResponse;
use OCP\IRequest; use OCP\IRequest;
use OCP\IUserSession; use OCP\IUserSession;
use OCA\Theming\ThemingDefaults;
use OCA\SideMenu\Service\Color;
class CssController extends Controller class CssController extends Controller
{ {
@@ -40,12 +42,30 @@ class CssController extends Controller
*/ */
protected $user; protected $user;
public function __construct(string $appName, IRequest $request, ConfigProxy $config) /**
* @var ThemingDefaults
*/
protected $theming;
/**
* @var Color
*/
protected $color;
public function __construct(
string $appName,
IRequest $request,
ConfigProxy $config,
ThemingDefaults $theming,
Color $color
)
{ {
parent::__construct($appName, $request); parent::__construct($appName, $request);
$this->user = OC::$server[IUserSession::class]->getUser(); $this->user = OC::$server[IUserSession::class]->getUser();
$this->config = $config; $this->config = $config;
$this->theming = $theming;
$this->color = $color;
} }
/** /**
@@ -65,29 +85,80 @@ class CssController extends Controller
protected function getConfig(): array protected function getConfig(): array
{ {
$backgroundColor = $this->config->getAppValue('background-color', '#333333');
$backgroundColorTo = $this->config->getAppValue('background-color-to', $backgroundColor);
$isForced = $this->config->getAppValueBool('force', '0'); $isForced = $this->config->getAppValueBool('force', '0');
$topMenuApps = $this->config->getAppValueArray('top-menu-apps', '[]'); $topMenuApps = $this->config->getAppValueArray('top-menu-apps', '[]');
$topSideMenuApps = $this->config->getAppValueArray('top-side-menu-apps', '[]');
$isAccessibilityAppEnabled = $this->config->getAppValueBool('enabled', '0', 'accessibility');
$isBreezeDarkAppEnabled = $this->config->getAppValueBool('enabled', '0', 'breezedark');
$isBreezeDarkGlobalEnabled = $this->config->getAppValueBool('theme_enabled', '0', 'breezedark');
if ($this->user) { if ($this->user) {
$userTopMenuApps = $this->config->getUserValueArray($this->user, 'top-menu-apps', '[]'); $userTopMenuApps = $this->config->getUserValueArray($this->user, 'top-menu-apps', '[]');
$userTopSideMenuApps = $this->config->getUserValueArray($this->user, 'top-side-menu-apps', '[]');
if (!empty($userTopMenuApps) && !$isForced) { if (!empty($userTopMenuApps) && !$isForced) {
$topMenuApps = $userTopMenuApps; $topMenuApps = $userTopMenuApps;
} }
if (!empty($userTopSideMenuApps) && !$isForced) {
$topSideMenuApps = $userTopSideMenuApps;
}
$isDarkThemeUserEnabled = $this->config->getUserValue($this->user, 'theme', '', 'accessibility') === 'dark';
$isBreezeDarkUserEnabled = $this->config->getUserValue($this->user, 'theme_enabled', '', 'breezedark');
$isBreezeDarkUserEnabled = $isBreezeDarkUserEnabled === '1' || ($isBreezeDarkGlobalEnabled && $isBreezeDarkUserEnabled === '');
} else {
$isDarkThemeUserEnabled = false;
$isBreezeDarkUserEnabled = false;
} }
$isDarkMode = ($isAccessibilityAppEnabled && $isDarkThemeUserEnabled) || ($isBreezeDarkAppEnabled && $isBreezeDarkUserEnabled);
$primaryColor = $this->theming->getColorPrimary();
$lightenPrimaryColor = $this->color->adjustBrightness($primaryColor, 0.2);
$darkenPrimaryColor = $this->color->adjustBrightness($primaryColor, -0.2);
$darkenPrimaryColor2 = $this->color->adjustBrightness($primaryColor, -0.3);
$textColor = $this->theming->getTextColorPrimary();
if ($isDarkMode) {
$backgroundColor = $this->config->getAppValue('dark-mode-background-color', $darkenPrimaryColor);
$backgroundColorTo = $this->config->getAppValue('dark-mode-background-color-to', $darkenPrimaryColor);
$currentAppBackgroundColor = $this->config->getAppValue('dark-mode-current-app-background-color', $darkenPrimaryColor2);
$loaderColor = $this->config->getAppValue('dark-mode-loader-color', $lightenPrimaryColor);
$textColor = $this->config->getAppValue('dark-mode-text-color', $textColor);
$iconInvertFilter = abs($this->config->getAppValueInt('dark-mode-icon-invert-filter', '0')).'%';
$iconOpacity = abs($this->config->getAppValueInt('dark-mode-icon-opacity', '100') / 100);
$opener = $this->config->getAppValue('dark-mode-opener', 'side-menu-opener');
$backgroundOpacity = dechex($this->config->getAppValueInt('dark-mode-background-color-opacity', '100') * 255 / 100);
} else {
$backgroundColor = $this->config->getAppValue('background-color', $darkenPrimaryColor);
$backgroundColorTo = $this->config->getAppValue('background-color-to', $darkenPrimaryColor);
$currentAppBackgroundColor = $this->config->getAppValue('current-app-background-color', $darkenPrimaryColor2);
$loaderColor = $this->config->getAppValue('loader-color', $lightenPrimaryColor);
$textColor = $this->config->getAppValue('text-color', $textColor);
$iconInvertFilter = abs($this->config->getAppValueInt('icon-invert-filter', '0')).'%';
$iconOpacity = abs($this->config->getAppValueInt('icon-opacity', '100') / 100);
$opener = $this->config->getAppValue('opener', 'side-menu-opener');
$backgroundOpacity = dechex($this->config->getAppValueInt('background-color-opacity', '100') * 255 / 100);
}
$backgroundColor .= $backgroundOpacity;
$backgroundColorTo .= $backgroundOpacity;
return [ return [
'vars' => [ 'vars' => [
'background-color' => $backgroundColor, 'background-color' => $backgroundColor,
'background-color-to' => $backgroundColorTo, 'background-color-to' => $backgroundColorTo,
'current-app-background-color' => $this->config->getAppValue('current-app-background-color', '#444444'), 'current-app-background-color' => $currentAppBackgroundColor,
'loader-color' => $this->config->getAppValue('loader-color', '#0e75ac'), 'loader-color' => $loaderColor,
'text-color' => $this->config->getAppValue('text-color', '#FFFFFF'), 'text-color' => $textColor,
'opener' => $this->config->getAppValue('opener', 'side-menu-opener'), 'opener' => $opener,
'icon-invert-filter' => abs($this->config->getAppValueInt('icon-invert-filter', '0')).'%', 'icon-invert-filter' => $iconInvertFilter,
'icon-opacity' => abs($this->config->getAppValueInt('icon-opacity', '100') / 100), 'icon-opacity' => $iconOpacity,
], ],
'display-logo' => $this->config->getAppValueBool('display-logo', '1'), 'display-logo' => $this->config->getAppValueBool('display-logo', '1'),
'opener-only' => $this->config->getAppValueBool('opener-only', '0'), 'opener-only' => $this->config->getAppValueBool('opener-only', '0'),
@@ -97,6 +168,7 @@ class CssController extends Controller
'always-displayed' => $this->config->getAppValueBool('always-displayed', '0'), 'always-displayed' => $this->config->getAppValueBool('always-displayed', '0'),
'big-menu' => $this->config->getAppValueBool('big-menu', '0'), 'big-menu' => $this->config->getAppValueBool('big-menu', '0'),
'top-menu-apps' => $topMenuApps, 'top-menu-apps' => $topMenuApps,
'top-side-menu-apps' => $topSideMenuApps,
]; ];
} }
} }
+8
View File
@@ -94,6 +94,7 @@ class JsController extends Controller
protected function getConfig(): array protected function getConfig(): array
{ {
$topMenuApps = $this->config->getAppValueArray('top-menu-apps', '[]'); $topMenuApps = $this->config->getAppValueArray('top-menu-apps', '[]');
$topSideMenuApps = $this->config->getAppValueArray('top-side-menu-apps', '[]');
$targetBlankApps = $this->config->getAppValueArray('target-blank-apps', '[]'); $targetBlankApps = $this->config->getAppValueArray('target-blank-apps', '[]');
$useAvatar = $this->config->getAppValueBool('use-avatar', '0'); $useAvatar = $this->config->getAppValueBool('use-avatar', '0');
$isForced = $this->config->getAppValueBool('force', '0'); $isForced = $this->config->getAppValueBool('force', '0');
@@ -103,11 +104,16 @@ class JsController extends Controller
if ($this->user) { if ($this->user) {
$userTopMenuApps = $this->config->getUserValueArray($this->user, 'top-menu-apps', '[]'); $userTopMenuApps = $this->config->getUserValueArray($this->user, 'top-menu-apps', '[]');
$userTopSideMenuApps = $this->config->getUserValueArray($this->user, 'top-side-menu-apps', '[]');
if (!empty($userTopMenuApps) && !$isForced) { if (!empty($userTopMenuApps) && !$isForced) {
$topMenuApps = $userTopMenuApps; $topMenuApps = $userTopMenuApps;
} }
if (!empty($userTopSideMenuApps) && !$isForced) {
$topSideMenuApps = $userTopSideMenuApps;
}
$userTargetBlankMode = $this->config->getUserValueInt($this->user, 'target-blank-mode', '1'); $userTargetBlankMode = $this->config->getUserValueInt($this->user, 'target-blank-mode', '1');
$userTargetBlankApps = $this->config->getUserValueArray($this->user, 'target-blank-apps', '[]'); $userTargetBlankApps = $this->config->getUserValueArray($this->user, 'target-blank-apps', '[]');
@@ -152,10 +158,12 @@ class JsController extends Controller
'hide-when-no-apps' => $this->config->getAppValueBool('hide-when-no-apps', '0'), 'hide-when-no-apps' => $this->config->getAppValueBool('hide-when-no-apps', '0'),
'loader-enabled' => $this->config->getAppValueBool('loader-enabled', '1'), 'loader-enabled' => $this->config->getAppValueBool('loader-enabled', '1'),
'always-displayed' => $this->config->getAppValueBool('always-displayed', '0'), 'always-displayed' => $this->config->getAppValueBool('always-displayed', '0'),
'side-with-categories' => $this->config->getAppValueBool('side-with-categories', '0'),
'big-menu' => $this->config->getAppValueBool('big-menu', '0'), 'big-menu' => $this->config->getAppValueBool('big-menu', '0'),
'big-menu-hidden-apps' => $this->config->getAppValueArray('big-menu-hidden-apps', '[]'), 'big-menu-hidden-apps' => $this->config->getAppValueArray('big-menu-hidden-apps', '[]'),
'avatar' => $avatar, 'avatar' => $avatar,
'top-menu-apps' => $topMenuApps, 'top-menu-apps' => $topMenuApps,
'top-side-menu-apps' => $topSideMenuApps,
'target-blank-apps' => $targetBlankApps, 'target-blank-apps' => $targetBlankApps,
'settings' => $settings, 'settings' => $settings,
'logo' => $this->themingDefaults->getLogo(), 'logo' => $this->themingDefaults->getLogo(),
+30 -43
View File
@@ -21,13 +21,11 @@ namespace OCA\SideMenu\Controller;
use OC; use OC;
use OC\App\AppStore\Fetcher\CategoryFetcher; use OC\App\AppStore\Fetcher\CategoryFetcher;
use OC\URLGenerator; use OC\URLGenerator;
use OCA\SideMenu\AppInfo\Application;
use OCA\SideMenu\Service\AppRepository; use OCA\SideMenu\Service\AppRepository;
use OCA\SideMenu\Service\CategoryRepository;
use OCA\SideMenu\Service\ConfigProxy; use OCA\SideMenu\Service\ConfigProxy;
use OCP\AppFramework\Controller; use OCP\AppFramework\Controller;
use OCP\AppFramework\Http\JSONResponse; use OCP\AppFramework\Http\JSONResponse;
use OCP\IConfig;
use OCP\IL10N;
use OCP\IRequest; use OCP\IRequest;
use OCP\IUserSession; use OCP\IUserSession;
use OCP\L10N\IFactory; use OCP\L10N\IFactory;
@@ -39,11 +37,6 @@ class NavController extends Controller
*/ */
protected $config; protected $config;
/**
* @var IConfig
*/
protected $iConfig;
/** /**
* @var AppRepository * @var AppRepository
*/ */
@@ -67,20 +60,17 @@ class NavController extends Controller
public function __construct( public function __construct(
string $appName, string $appName,
IRequest $request, IRequest $request,
IConfig $iConfig,
ConfigProxy $config, ConfigProxy $config,
AppRepository $appRepository, AppRepository $appRepository,
CategoryFetcher $categoryFetcher, CategoryRepository $categoryRepository,
URLGenerator $router, URLGenerator $router,
IL10N $trans,
IFactory $l10nFactory IFactory $l10nFactory
) { ) {
parent::__construct($appName, $request); parent::__construct($appName, $request);
$this->config = $config; $this->config = $config;
$this->iConfig = $iConfig;
$this->appRepository = $appRepository; $this->appRepository = $appRepository;
$this->categoryFetcher = $categoryFetcher; $this->categoryRepository = $categoryRepository;
$this->l10nFactory = $l10nFactory; $this->l10nFactory = $l10nFactory;
$this->router = $router; $this->router = $router;
} }
@@ -94,14 +84,7 @@ class NavController extends Controller
*/ */
public function items() public function items()
{ {
$apps = $this->appRepository->getVisibleApps();
$currentLanguage = substr($this->l10nFactory->findLanguage(), 0, 2);
$externalSitesInTopMenu = $this->config->getAppValueBool('external-sites-in-top-menu', '0');
$hiddenApps = $this->config->getAppValueArray('big-menu-hidden-apps', '[]');
$user = OC::$server[IUserSession::class]->getUser(); $user = OC::$server[IUserSession::class]->getUser();
$isForced = $this->config->getAppValueBool('force', '0');
$appsCategories = [];
$categoriesAppsCount = [];
$items = []; $items = [];
if (!$user) { if (!$user) {
@@ -110,34 +93,19 @@ class NavController extends Controller
]); ]);
} }
$categoriesLabels = $this->config->getAppValueArray('cache-categories', '[]'); $apps = $this->appRepository->getVisibleApps();
$categoriesLabels = $this->categoryRepository->getOrderedCategories();
if (empty($categoriesLabels)) { $hiddenApps = $this->config->getAppValueArray('big-menu-hidden-apps', '[]');
$categoriesLabels = $this->categoryFetcher->get(); $isForced = $this->config->getAppValueBool('force', '0');
$this->iConfig->setAppValue(Application::APP_ID, 'cache-categories', json_encode($categoriesLabels));
}
$topMenuApps = $this->config->getAppValueArray('top-menu-apps', '[]'); $topMenuApps = $this->config->getAppValueArray('top-menu-apps', '[]');
$userTopMenuApps = $this->config->getUserValueArray($user, 'top-menu-apps', '[]'); $userTopMenuApps = $this->config->getUserValueArray($user, 'top-menu-apps', '[]');
$appsCategories = [];
$categoriesAppsCount = [];
if (!$isForced && !empty($userTopMenuApps)) { if (!$isForced && !empty($userTopMenuApps)) {
$topMenuApps = $userTopMenuApps; $topMenuApps = $userTopMenuApps;
} }
foreach ($categoriesLabels as $k => $category) {
$categoriesLabels[$category['id']] = $category['translations'][$currentLanguage]['name'] ?? $category['translations']['en']['name'];
unset($categoriesLabels[$k]);
}
$categoriesLabels['external_links'] = $this->l10nFactory->get('external')->t('External sites');
$items['other'] = [
'name' => '',
'apps' => [],
];
foreach ($apps as $app) { foreach ($apps as $app) {
if (in_array($app['id'], $topMenuApps)) { if (in_array($app['id'], $topMenuApps)) {
continue; continue;
@@ -158,6 +126,7 @@ class NavController extends Controller
if (!isset($items[$category])) { if (!isset($items[$category])) {
$items[$category] = [ $items[$category] = [
'name' => $categoriesLabels[$category] ?? $category, 'name' => $categoriesLabels[$category] ?? $category,
'categoryId' => $category,
'apps' => [], 'apps' => [],
]; ];
} }
@@ -216,8 +185,26 @@ class NavController extends Controller
} }
} }
usort($items, function ($a, $b) { usort($items, function ($a, $b) use ($categoriesLabels) {
return ($a['name'] < $b['name']) ? -1 : 1; foreach ($categoriesLabels as $key => $value) {
if ($a['categoryId'] === 'other') {
return -1;
}
if ($b['categoryId'] === 'other') {
return 1;
}
if ($a['categoryId'] === $key) {
return -1;
}
if ($b['categoryId'] === $key) {
return 1;
}
}
return 0;
}); });
return new JSONResponse([ return new JSONResponse([
+4 -3
View File
@@ -18,13 +18,13 @@
namespace OCA\SideMenu\Controller; namespace OCA\SideMenu\Controller;
use OCA\SideMenu\AppInfo\Application;
use OCA\SideMenu\Service\ConfigProxy;
use OCP\AppFramework\Controller; use OCP\AppFramework\Controller;
use OCP\AppFramework\Http\Response; use OCP\AppFramework\Http\Response;
use OCP\IConfig; use OCP\IConfig;
use OCP\IRequest; use OCP\IRequest;
use OCP\IUserSession; use OCP\IUserSession;
use OCA\SideMenu\AppInfo\Application;
use OCA\SideMenu\Service\ConfigProxy;
class PersonalSettingController extends Controller class PersonalSettingController extends Controller
{ {
@@ -54,6 +54,7 @@ class PersonalSettingController extends Controller
/** /**
* @NoAdminRequired * @NoAdminRequired
* @NoCSRFRequired
* *
* @param mixed $name * @param mixed $name
* @param mixed $value * @param mixed $value
@@ -96,7 +97,7 @@ class PersonalSettingController extends Controller
} }
} }
if ('top-menu-apps' === $name) { if (in_array($name, ['top-menu-apps', 'top-side-menu-apps'])) {
$doSave = true; $doSave = true;
$data = json_decode($value, true); $data = json_decode($value, true);
+27 -1
View File
@@ -21,10 +21,27 @@ class AppRepository
*/ */
protected $l10nFactory; protected $l10nFactory;
public function __construct(\OC_App $ocApp, IFactory $l10nFactory) /**
* @var ConfigProxy
*/
protected $config;
/**
* @var CategoryRepository
*/
protected $categoryRepository;
public function __construct(
\OC_App $ocApp,
IFactory $l10nFactory,
ConfigProxy $config,
CategoryRepository $categoryRepository
)
{ {
$this->ocApp = $ocApp; $this->ocApp = $ocApp;
$this->l10nFactory = $l10nFactory; $this->l10nFactory = $l10nFactory;
$this->config = $config;
$this->categoryRepository = $categoryRepository;
} }
/** /**
@@ -35,6 +52,9 @@ class AppRepository
public function getVisibleApps() public function getVisibleApps()
{ {
$navigation = $this->ocApp->getNavigation(); $navigation = $this->ocApp->getNavigation();
$appCategoriesCustom = $this->config->getAppValueArray('apps-categories-custom', '[]');
$categoriesCustom = $this->config->getAppValueArray('categories-custom', '[]');
$categories = $this->categoryRepository->getOrderedCategories();
$apps = $this->ocApp->listAllApps(); $apps = $this->ocApp->listAllApps();
$visibleApps = []; $visibleApps = [];
@@ -74,6 +94,12 @@ class AppRepository
} }
} }
foreach ($visibleApps as $id => $app) {
if (isset($appCategoriesCustom[$id], $categories[$appCategoriesCustom[$id]])) {
$visibleApps[$id]['category'] = [$appCategoriesCustom[$id]];
}
}
usort($visibleApps, function ($a, $b) { usort($visibleApps, function ($a, $b) {
return ($a['name'] < $b['name']) ? -1 : 1; return ($a['name'] < $b['name']) ? -1 : 1;
}); });
+119
View File
@@ -0,0 +1,119 @@
<?php
namespace OCA\SideMenu\Service;
use OC\App\AppStore\Fetcher\CategoryFetcher;
use OCA\SideMenu\AppInfo\Application;
use OCP\IConfig;
use OCP\IUserSession;
use OCP\L10N\IFactory;
/**
* class CategoryRepository.
*
* @author Simon Vieille <simon@deblan.fr>
*/
class CategoryRepository
{
/**
* @var CategoryFetcher
*/
protected $categoryFetcher;
/**
* @var IFactory
*/
protected $l10nFactory;
/**
* @var ConfigProxy
*/
protected $config;
/**
* @var IConfig
*/
protected $iConfig;
/**
* @var IUserSession
*/
protected $userSession;
public function __construct(
CategoryFetcher $categoryFetcher,
ConfigProxy $config,
IConfig $iConfig,
IFactory $l10nFactory,
IUserSession $userSession
) {
$this->categoryFetcher = $categoryFetcher;
$this->l10nFactory = $l10nFactory;
$this->config = $config;
$this->iConfig = $iConfig;
$this->userSession = $userSession;
}
/**
* Retrieves categories.
*
* @return array
*/
public function getOrderedCategories()
{
$currentLanguage = substr($this->l10nFactory->findLanguage(), 0, 2);
$type = $this->config->getAppValue('categories-order-type', 'default');
$order = $this->config->getAppValueArray('categories-order', '[]');
$categoriesLabels = $this->config->getAppValueArray('cache-categories', '[]');
$customCategories = $this->config->getAppValueArray('categories-custom', '[]');
if (empty($categoriesLabels)) {
$categoriesLabels = $this->categoryFetcher->get();
$this->iConfig->setAppValue(Application::APP_ID, 'cache-categories', json_encode($categoriesLabels));
}
foreach ($categoriesLabels as $k => $category) {
$categoriesLabels[$category['id']] = $category['translations'][$currentLanguage]['name'] ?? $category['translations']['en']['name'];
unset($categoriesLabels[$k]);
}
$categoriesLabels['external_links'] = $this->l10nFactory->get('external')->t('External sites');
$categoriesLabels['other'] = '';
$user = $this->userSession->getUser();
if ($user) {
$lang = $this->iConfig->getUserValue($user->getUid(), 'core', 'lang');
} else {
$lang = 'en';
}
foreach ($customCategories as $category) {
$categoriesLabels[$category['id']] = $category[$lang] ?? $category['en'];
}
asort($categoriesLabels);
if ('custom' === $type) {
$ordered = [];
foreach ($order as $id) {
if (isset($categoriesLabels[$id])) {
$ordered[$id] = $categoriesLabels[$id];
}
}
foreach ($categoriesLabels as $id => $value) {
if (!isset($ordered[$id])) {
$ordered[$id] = $value;
}
}
$categoriesLabels = $ordered;
}
return $categoriesLabels;
}
}
+34
View File
@@ -0,0 +1,34 @@
<?php
namespace OCA\SideMenu\Service;
/**
* class Color.
*
* @author Simon Vieille <simon@deblan.fr>
*/
class Color
{
/**
* @thanks https://stackoverflow.com/posts/54393956/revision
*/
public function adjustBrightness(string $hexCode, float $adjustPercent): string
{
$hexCode = ltrim($hexCode, '#');
if (3 == strlen($hexCode)) {
$hexCode = $hexCode[0].$hexCode[0].$hexCode[1].$hexCode[1].$hexCode[2].$hexCode[2];
}
$hexCode = array_map('hexdec', str_split($hexCode, 2));
foreach ($hexCode as &$color) {
$adjustableLimit = $adjustPercent < 0 ? $color : 255 - $color;
$adjustAmount = ceil($adjustableLimit * $adjustPercent);
$color = str_pad(dechex($color + $adjustAmount), 2, '0', STR_PAD_LEFT);
}
return '#'.implode($hexCode);
}
}
+20 -16
View File
@@ -23,43 +23,47 @@ class ConfigProxy
$this->config = $config; $this->config = $config;
} }
public function getAppValue(string $name, string $default): string public function getAppValue(string $name, string $default, $appId = null): string
{ {
return (string) $this->config->getAppValue(Application::APP_ID, $name, $default); return (string) $this->config->getAppValue($appId ?? Application::APP_ID, $name, $default);
} }
public function getUserValue(User $user, string $name, string $default): string public function getUserValue(User $user, string $name, string $default, $appId = null): string
{ {
return (string) $this->config->getUserValue($user->getUid(), Application::APP_ID, $name, $default); return (string) $this->config->getUserValue($user->getUid(), $appId ?? Application::APP_ID, $name, $default);
} }
public function getAppValueBool(string $name, string $default): bool public function getAppValueBool(string $name, string $default, $appId = null): bool
{ {
return (bool) $this->getAppValue($name, $default); return (bool) $this->getAppValue($name, $default, $appId);
} }
public function getAppValueArray(string $name, string $default): array public function getAppValueArray(string $name, string $default, $appId = null): array
{ {
return (array) json_decode($this->getAppValue($name, $default), true); return (array) json_decode($this->getAppValue($name, $default, $appId), true);
} }
public function getAppValueInt(string $name, string $default): int public function getAppValueInt(string $name, string $default, $appId = null): int
{ {
return (int) $this->getAppValue($name, $default); return (int) $this->getAppValue($name, $default, $appId);
} }
public function getUserValueBool(User $user, string $name, string $default): bool public function getUserValueBool(User $user, string $name, string $default, $appId = null): bool
{ {
return (bool) $this->getUserValue($user, $name, $default); return (bool) str_replace(
['yes', 'no'],
['1', '0'],
$this->getUserValue($user, $name, $default, $appId)
);
} }
public function getUserValueArray(User $user, string $name, string $default): array public function getUserValueArray(User $user, string $name, string $default, $appId = null): array
{ {
return (array) json_decode($this->getUserValue($user, $name, $default), true); return (array) json_decode($this->getUserValue($user, $name, $default, $appId), true);
} }
public function getUserValueInt(User $user, string $name, string $default): int public function getUserValueInt(User $user, string $name, string $default, $appId = null): int
{ {
return (int) $this->getUserValue($user, $name, $default); return (int) $this->getUserValue($user, $name, $default, $appId);
} }
} }
+48
View File
@@ -0,0 +1,48 @@
<?php
namespace OCA\SideMenu\Service;
use OCP\IDBConnection;
/**
* class LangRepository.
*
* @author Simon Vieille <simon@deblan.fr>
*/
class LangRepository
{
/**
* @var IDBConnection
*/
protected $db;
public function __construct(IDBConnection $db)
{
$this->db = $db;
}
public function getUsedLangs(): array
{
$qb = $this->db->getQueryBuilder();
$qb->select($qb->createFunction('DISTINCT configvalue'))
->where('configkey=:configkey and appid=:appid and configvalue<>:configvalue')
->setParameters([
'configkey' => 'lang',
'appid' => 'core',
'configvalue' => 'en',
])
->from('preferences')
;
$stmt = $qb->execute();
$langs = ['en'];
foreach ($stmt->fetchAll() as $result) {
$langs[] = $result['configvalue'];
}
return $langs;
}
}
+84 -9
View File
@@ -20,11 +20,15 @@ namespace OCA\SideMenu\Settings;
use OCA\SideMenu\AppInfo\Application; use OCA\SideMenu\AppInfo\Application;
use OCA\SideMenu\Service\AppRepository; use OCA\SideMenu\Service\AppRepository;
use OCA\SideMenu\Service\CategoryRepository;
use OCA\SideMenu\Service\ConfigProxy; use OCA\SideMenu\Service\ConfigProxy;
use OCP\AppFramework\Http\TemplateResponse; use OCP\AppFramework\Http\TemplateResponse;
use OCP\IL10N; use OCP\IL10N;
use OCP\ILogger; use OCP\ILogger;
use OCP\Settings\ISettings; use OCP\Settings\ISettings;
use OCA\Theming\ThemingDefaults;
use OCA\SideMenu\Service\Color;
use OCA\SideMenu\Service\LangRepository;
class Admin implements ISettings class Admin implements ISettings
{ {
@@ -48,12 +52,44 @@ class Admin implements ISettings
*/ */
private $appRepository; private $appRepository;
public function __construct(IL10N $l, ILogger $logger, ConfigProxy $config, AppRepository $appRepository) /**
{ * @var CategoryRepository
*/
private $categoryRepository;
/**
* @var ThemingDefaults
*/
protected $theming;
/**
* @var Color
*/
protected $color;
/**
* @var LangRepository
*/
protected $langRepository;
public function __construct(
IL10N $l,
ILogger $logger,
ConfigProxy $config,
AppRepository $appRepository,
CategoryRepository $categoryRepository,
ThemingDefaults $theming,
Color $color,
LangRepository $langRepository
) {
$this->l = $l; $this->l = $l;
$this->logger = $logger; $this->logger = $logger;
$this->config = $config; $this->config = $config;
$this->appRepository = $appRepository; $this->appRepository = $appRepository;
$this->categoryRepository = $categoryRepository;
$this->theming = $theming;
$this->color = $color;
$this->langRepository = $langRepository;
} }
/** /**
@@ -61,22 +97,54 @@ class Admin implements ISettings
*/ */
public function getForm() public function getForm()
{ {
$backgroundColor = $this->config->getAppValue('background-color', '#333333'); $primaryColor = $this->theming->getColorPrimary();
$backgroundColorTo = $this->config->getAppValue('background-color-to', $backgroundColor); $lightenPrimaryColor = $this->color->adjustBrightness($primaryColor, 0.2);
$darkenPrimaryColor = $this->color->adjustBrightness($primaryColor, -0.2);
$darkenPrimaryColor2 = $this->color->adjustBrightness($primaryColor, -0.3);
$textColor = $this->theming->getTextColorPrimary();
$backgroundColor = $this->config->getAppValue('background-color', $darkenPrimaryColor);
$backgroundColorTo = $this->config->getAppValue('background-color-to', $darkenPrimaryColor);
$darkModeBackgroundColor = $this->config->getAppValue('dark-mode-background-color', $darkenPrimaryColor);
$darkModeBackgroundColorTo = $this->config->getAppValue('dark-mode-background-color-to', $darkenPrimaryColor);
$parameters = [ $parameters = [
'defaults' => [
'background-color' => $darkenPrimaryColor,
'background-color-to' => $darkenPrimaryColor,
'current-app-background-color' => $darkenPrimaryColor2,
'text-color' => $textColor,
'loader-color' => $lightenPrimaryColor,
'dark-mode-background-color' => $darkenPrimaryColor,
'dark-mode-background-color-to' => $darkenPrimaryColor,
'dark-mode-current-app-background-color' => $darkenPrimaryColor2,
'dark-mode-text-color' => $textColor,
'dark-mode-loader-color' => $textColor,
],
'background-color' => $backgroundColor, 'background-color' => $backgroundColor,
'background-color-to' => $backgroundColorTo, 'background-color-to' => $backgroundColorTo,
'current-app-background-color' => $this->config->getAppValue('current-app-background-color', '#444444'), 'background-color-opacity' => $this->config->getAppValueInt('background-color-opacity', '100'),
'loader-color' => $this->config->getAppValue('loader-color', '#0e75ac'), 'current-app-background-color' => $this->config->getAppValue('current-app-background-color', $darkenPrimaryColor2),
'loader-color' => $this->config->getAppValue('loader-color', $lightenPrimaryColor),
'icon-invert-filter' => $this->config->getAppValueInt('icon-invert-filter', '0'), 'icon-invert-filter' => $this->config->getAppValueInt('icon-invert-filter', '0'),
'icon-opacity' => $this->config->getAppValueInt('icon-opacity', '100'), 'icon-opacity' => $this->config->getAppValueInt('icon-opacity', '100'),
'loader-enabled' => $this->config->getAppValue('loader-enabled', '1'), 'text-color' => $this->config->getAppValue('text-color', $textColor),
'text-color' => $this->config->getAppValue('text-color', '#FFFFFF'), 'dark-mode-background-color' => $darkModeBackgroundColor,
'cache' => $this->config->getAppValue('cache', '0'), 'dark-mode-background-color-to' => $darkModeBackgroundColorTo,
'dark-mode-background-color-opacity' => $this->config->getAppValueInt('dark-mode-background-color-opacity', '100'),
'dark-mode-current-app-background-color' => $this->config->getAppValue('dark-mode-current-app-background-color', $darkenPrimaryColor2),
'dark-mode-loader-color' => $this->config->getAppValue('dark-mode-loader-color', $textColor),
'dark-mode-icon-invert-filter' => $this->config->getAppValueInt('dark-mode-icon-invert-filter', '0'),
'dark-mode-icon-opacity' => $this->config->getAppValueInt('dark-mode-icon-opacity', '100'),
'dark-mode-text-color' => $this->config->getAppValue('dark-mode-text-color', $textColor),
'dark-mode-opener' => $this->config->getAppValue('dark-mode-opener', 'side-menu-opener'),
'opener' => $this->config->getAppValue('opener', 'side-menu-opener'), 'opener' => $this->config->getAppValue('opener', 'side-menu-opener'),
'loader-enabled' => $this->config->getAppValue('loader-enabled', '1'),
'cache' => $this->config->getAppValue('cache', '0'),
'always-displayed' => $this->config->getAppValue('always-displayed', '0'), 'always-displayed' => $this->config->getAppValue('always-displayed', '0'),
'big-menu' => $this->config->getAppValue('big-menu', '0'), 'big-menu' => $this->config->getAppValue('big-menu', '0'),
'side-with-categories' => $this->config->getAppValue('side-with-categories', '0'),
'big-menu-hidden-apps' => $this->config->getAppValueArray('big-menu-hidden-apps', '[]'), 'big-menu-hidden-apps' => $this->config->getAppValueArray('big-menu-hidden-apps', '[]'),
'display-logo' => $this->config->getAppValue('display-logo', '1'), 'display-logo' => $this->config->getAppValue('display-logo', '1'),
'add-logo-link' => $this->config->getAppValue('add-logo-link', '1'), 'add-logo-link' => $this->config->getAppValue('add-logo-link', '1'),
@@ -91,8 +159,15 @@ class Admin implements ISettings
'force' => $this->config->getAppValue('force', '0'), 'force' => $this->config->getAppValue('force', '0'),
'target-blank-apps' => $this->config->getAppValueArray('target-blank-apps', '[]'), 'target-blank-apps' => $this->config->getAppValueArray('target-blank-apps', '[]'),
'top-menu-apps' => $this->config->getAppValueArray('top-menu-apps', '[]'), 'top-menu-apps' => $this->config->getAppValueArray('top-menu-apps', '[]'),
'top-side-menu-apps' => $this->config->getAppValueArray('top-side-menu-apps', '[]'),
'default-enabled' => $this->config->getAppValue('default-enabled', '1'), 'default-enabled' => $this->config->getAppValue('default-enabled', '1'),
'apps' => $this->appRepository->getVisibleApps(), 'apps' => $this->appRepository->getVisibleApps(),
'apps-categories-custom' => $this->config->getAppValueArray('apps-categories-custom', '[]'),
'categories-order-type' => $this->config->getAppValue('categories-order-type', 'default'),
'categories-order' => $this->config->getAppValueArray('categories-order', '[]'),
'categories-custom' => $this->config->getAppValueArray('categories-custom', '[]'),
'categories' => $this->categoryRepository->getOrderedCategories(),
'langs' => $this->langRepository->getUsedLangs(),
]; ];
return new TemplateResponse(Application::APP_ID, 'settings/admin-form', $parameters, ''); return new TemplateResponse(Application::APP_ID, 'settings/admin-form', $parameters, '');
+4 -8
View File
@@ -18,10 +18,10 @@
namespace OCA\SideMenu\Settings; namespace OCA\SideMenu\Settings;
use OCA\SideMenu\AppInfo\Application;
use OCP\IL10N; use OCP\IL10N;
use OCP\IURLGenerator; use OCP\IURLGenerator;
use OCP\Settings\IIconSection; use OCP\Settings\IIconSection;
use OCA\SideMenu\AppInfo\Application;
class AdminSection implements IIconSection class AdminSection implements IIconSection
{ {
@@ -35,10 +35,6 @@ class AdminSection implements IIconSection
*/ */
private $url; private $url;
/**
* @param IURLGenerator $url
* @param IL10N $l
*/
public function __construct(IURLGenerator $url, IL10N $l) public function __construct(IURLGenerator $url, IL10N $l)
{ {
$this->url = $url; $this->url = $url;
@@ -47,7 +43,7 @@ class AdminSection implements IIconSection
/** /**
* returns the ID of the section. It is supposed to be a lower case string, * returns the ID of the section. It is supposed to be a lower case string,
* e.g. 'ldap' * e.g. 'ldap'.
* *
* @returns string * @returns string
*/ */
@@ -69,8 +65,8 @@ class AdminSection implements IIconSection
/** /**
* @return int whether the form should be rather on the top or bottom of * @return int whether the form should be rather on the top or bottom of
* the settings navigation. The sections are arranged in ascending order of * the settings navigation. The sections are arranged in ascending order of
* the priority values. It is required to return a value between 0 and 99. * the priority values. It is required to return a value between 0 and 99.
* *
* E.g.: 70 * E.g.: 70
*/ */
+1
View File
@@ -78,6 +78,7 @@ class Personal implements ISettings
$this->config->getAppValue('default-enabled', '1') $this->config->getAppValue('default-enabled', '1')
), ),
'top-menu-apps' => $this->config->getUserValueArray($user, 'top-menu-apps', '[]'), 'top-menu-apps' => $this->config->getUserValueArray($user, 'top-menu-apps', '[]'),
'top-side-menu-apps' => $this->config->getUserValueArray($user, 'top-side-menu-apps', '[]'),
'target-blank-mode' => $this->config->getUserValue($user, 'target-blank-mode', '1'), 'target-blank-mode' => $this->config->getUserValue($user, 'target-blank-mode', '1'),
'target-blank-apps' => $this->config->getUserValueArray($user, 'target-blank-apps', '[]'), 'target-blank-apps' => $this->config->getUserValueArray($user, 'target-blank-apps', '[]'),
'apps' => $this->appRepository->getVisibleApps(), 'apps' => $this->appRepository->getVisibleApps(),
+22 -7
View File
@@ -18,10 +18,11 @@
namespace OCA\SideMenu\Settings; namespace OCA\SideMenu\Settings;
use OCA\SideMenu\AppInfo\Application;
use OCA\SideMenu\Service\ConfigProxy;
use OCP\IL10N; use OCP\IL10N;
use OCP\IURLGenerator; use OCP\IURLGenerator;
use OCP\Settings\IIconSection; use OCP\Settings\IIconSection;
use OCA\SideMenu\AppInfo\Application;
class PersonalSection implements IIconSection class PersonalSection implements IIconSection
{ {
@@ -36,23 +37,29 @@ class PersonalSection implements IIconSection
private $url; private $url;
/** /**
* @param IURLGenerator $url * @var ConfigProxy
* @param IL10N $l
*/ */
public function __construct(IURLGenerator $url, IL10N $l) private $configProxy;
public function __construct(IURLGenerator $url, IL10N $l, ConfigProxy $configProxy)
{ {
$this->url = $url; $this->url = $url;
$this->l = $l; $this->l = $l;
$this->configProxy = $configProxy;
} }
/** /**
* returns the ID of the section. It is supposed to be a lower case string, * returns the ID of the section. It is supposed to be a lower case string,
* e.g. 'ldap' * e.g. 'ldap'.
* *
* @returns string * @returns string
*/ */
public function getID() public function getID()
{ {
if ($this->configProxy->getAppValueBool('force', '0')) {
return '';
}
return Application::APP_ID; return Application::APP_ID;
} }
@@ -64,18 +71,26 @@ class PersonalSection implements IIconSection
*/ */
public function getName() public function getName()
{ {
if ($this->configProxy->getAppValueBool('force', '0')) {
return '';
}
return $this->l->t(Application::APP_NAME); return $this->l->t(Application::APP_NAME);
} }
/** /**
* @return int whether the form should be rather on the top or bottom of * @return int whether the form should be rather on the top or bottom of
* the settings navigation. The sections are arranged in ascending order of * the settings navigation. The sections are arranged in ascending order of
* the priority values. It is required to return a value between 0 and 99. * the priority values. It is required to return a value between 0 and 99.
* *
* E.g.: 70 * E.g.: 70
*/ */
public function getPriority() public function getPriority()
{ {
if ($this->configProxy->getAppValueBool('force', '0')) {
return null;
}
return 70; return 70;
} }
+5 -5
View File
@@ -11,9 +11,9 @@
"stylelint:fix": "stylelint src --fix" "stylelint:fix": "stylelint src --fix"
}, },
"dependencies": { "dependencies": {
"@nextcloud/axios": "^1.3.2", "@nextcloud/axios": "^1.8.0",
"@nextcloud/vue": "^1.4.0", "@nextcloud/vue": "^1.5.0",
"axios": "^0.19.2", "axios": "^0.24.0",
"trim": "0.0.1", "trim": "0.0.1",
"vue": "^2.6.11" "vue": "^2.6.11"
}, },
@@ -21,7 +21,7 @@
"extends @nextcloud/browserslist-config" "extends @nextcloud/browserslist-config"
], ],
"engines": { "engines": {
"node": ">=10.0.0" "node": ">=16.0.0"
}, },
"devDependencies": { "devDependencies": {
"@babel/core": "^7.9.0", "@babel/core": "^7.9.0",
@@ -43,9 +43,9 @@
"eslint-plugin-standard": "^4.0.1", "eslint-plugin-standard": "^4.0.1",
"eslint-plugin-vue": "^5.2.3", "eslint-plugin-vue": "^5.2.3",
"file-loader": "^6.0.0", "file-loader": "^6.0.0",
"node-sass": "^4.13.1",
"sass-loader": "^8.0.2", "sass-loader": "^8.0.2",
"stylelint": "^8.4.0", "stylelint": "^8.4.0",
"sass": "^1.49.9",
"stylelint-config-recommended-scss": "^3.3.0", "stylelint-config-recommended-scss": "^3.3.0",
"stylelint-scss": "^3.16.0", "stylelint-scss": "^3.16.0",
"stylelint-webpack-plugin": "^0.10.5", "stylelint-webpack-plugin": "^0.10.5",
Binary file not shown.

Before

Width:  |  Height:  |  Size: 132 KiB

After

Width:  |  Height:  |  Size: 321 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 57 KiB

After

Width:  |  Height:  |  Size: 57 KiB

+182
View File
@@ -0,0 +1,182 @@
<!--
@license GNU AGPL version 3 or any later version
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
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
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/>.
-->
<template>
<div>
<ul class="side-menu-setting-list">
<li v-for="item in values" class="side-menu-setting-list-item" v-on:click="showEditForm(item)">
<span v-html="item.en"></span>
</li>
</ul>
<Actions>
<ActionButton @click="showAddForm" icon="icon-add"></ActionButton>
</Actions>
<Modal v-if="addForm" @close="hideAddForm">
<div class="modal__content">
<div v-for="lang in langs">
<span class="lang" v-html="lang"></span>
<input type="text" v-model="newValue[lang]" required>
</div>
<Actions>
<ActionButton @click="saveAdd" icon="icon-checkmark"></ActionButton>
</Actions>
</div>
</Modal>
<Modal v-if="editForm" @close="hideEditForm">
<div class="modal__content">
<div v-for="lang in langs">
<span class="lang" v-html="lang"></span>
<input type="text" v-model="editValue[lang]" required>
</div>
<div class="pull-right">
<Actions>
<ActionButton @click="removeEdit" icon="icon-delete"></ActionButton>
</Actions>
</div>
<Actions>
<ActionButton @click="saveEdit" icon="icon-checkmark"></ActionButton>
</Actions>
</div>
</Modal>
</div>
</template>
<style scoped>
.modal__content {
width: 200px;
padding: 10px;
}
.modal__content .lang {
width: 60px;
display: inline-block;
padding: 4px;
box-sizing: border-box;
}
.modal__content input[type=text] {
width: calc(100% - 85px);
display: inline-block;
margin: 0;
}
.pull-right {
float: right;
}
</style>
<script>
import Modal from '@nextcloud/vue/dist/Components/Modal'
import Actions from '@nextcloud/vue/dist/Components/Actions'
import ActionButton from '@nextcloud/vue/dist/Components/ActionButton'
export default {
name: 'AdminCategoriesCustom',
components: {
Modal,
Actions,
ActionButton,
},
data() {
return {
input: null,
values: [],
langs: [],
addForm: false,
editForm: false,
newValue: {},
editValue: {},
}
},
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
},
},
mounted() {
this.input = document.querySelector('input[name="categories-custom"]')
this.init()
}
}
</script>
+4 -4
View File
@@ -18,11 +18,9 @@
import Vue from 'vue' import Vue from 'vue'
import SideMenu from './SideMenu.vue' import SideMenu from './SideMenu.vue'
import SideMenuBig from './SideMenuBig.vue' import SideMenuBig from './SideMenuBig.vue'
import SideMenuWithCategories from './SideMenuWithCategories.vue'
// Vue.prototype.t = t
Vue.prototype.OC = OC Vue.prototype.OC = OC
// Vue.prototype.OC = OCP
const mountSideMenuComponent = () => { const mountSideMenuComponent = () => {
const sideMenuContainer = document.querySelector('#side-menu') const sideMenuContainer = document.querySelector('#side-menu')
@@ -32,6 +30,8 @@ const mountSideMenuComponent = () => {
if (sideMenuContainer.getAttribute('data-bigmenu')) { if (sideMenuContainer.getAttribute('data-bigmenu')) {
component = SideMenuBig component = SideMenuBig
} else if(sideMenuContainer.getAttribute('data-sidewithcategories')) {
component = SideMenuWithCategories
} else { } else {
component = SideMenu component = SideMenu
} }
@@ -41,7 +41,7 @@ const mountSideMenuComponent = () => {
sideMenu.$mount('#side-menu') sideMenu.$mount('#side-menu')
$('body').trigger('side-menu.ready') document.querySelector('body').dispatchEvent(new CustomEvent('side-menu.ready'))
} else { } else {
window.setTimeout(mountSideMenuComponent, 50) window.setTimeout(mountSideMenuComponent, 50)
} }
+6 -4
View File
@@ -100,7 +100,7 @@ export default {
var dataId = parent.getAttribute('data-id') var dataId = parent.getAttribute('data-id')
dataId = dataId !== null ? dataId : '' dataId = dataId !== null ? dataId : ''
if (!parent.classList.contains('app-hidden') && !menuIsHidden) { if (!parent.classList.contains('app-top-side-menu') && !parent.classList.contains('app-hidden') && !menuIsHidden) {
continue continue
} }
@@ -122,13 +122,15 @@ export default {
name: trim(element.querySelector('span').innerHTML), name: trim(element.querySelector('span').innerHTML),
icon: svg, icon: svg,
active: element.classList.contains('active') active: element.classList.contains('active')
}); })
} }
} }
(function(apps) { (function(apps) {
window.setTimeout(function() { window.setTimeout(function() {
jQuery('body').trigger('side-menu.apps', [apps]) document.querySelector('body').dispatchEvent(new CustomEvent('side-menu.apps', {
detail: {apps: apps},
}))
}, 1000) }, 1000)
})(this.apps) })(this.apps)
}, },
@@ -147,7 +149,7 @@ export default {
that.logo = config['logo'] that.logo = config['logo']
that.logoLink = config['logo-link'] that.logoLink = config['logo-link']
that.settings = config['settings'] that.settings = config['settings']
}); })
}, },
}, },
mounted() { mounted() {
+6 -4
View File
@@ -43,7 +43,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
v-bind:icon="app.icon" v-bind:icon="app.icon"
v-bind:label="app.name" v-bind:label="app.name"
v-bind:href="app.href" v-bind:href="app.href"
v-bind:target="targetBlankApps.indexOf(app.id) !== -1 ? '_blank' : undefined" v-bind:target="targetBlankApps.indexOf(appId) !== -1 ? '_blank' : undefined"
/> />
</ul> </ul>
</div> </div>
@@ -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() { retrieveActiveApp() {
@@ -116,7 +118,7 @@ export default {
that.targetBlankApps = config['target-blank-apps'] that.targetBlankApps = config['target-blank-apps']
that.settings = config['settings'] that.settings = config['settings']
}); })
}, },
}, },
mounted() { mounted() {
+128
View File
@@ -0,0 +1,128 @@
<!--
@license GNU AGPL version 3 or any later version
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
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
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/>.
-->
<template>
<div id="side-menu" class="side-menu-with-categories">
<div class="side-menu-header">
<SettingsButton
v-if="settings"
v-bind:href="settings.href"
v-bind:label="settings.name"
v-bind:avatar="settings.avatar"
/>
<OpenerButton />
</div>
<div class="side-menu-categories-wrapper">
<div class="side-menu-categories">
<Loader v-if="!items.length" />
<div class="side-menu-category" v-for="category in items">
<h2 class="side-menu-category-title" v-if="category.name != ''" v-html="category.name"></h2>
<ul class="side-menu-apps-list">
<SideMenuBigApp
v-for="(app, appId) in category.apps"
v-bind:classes="{'side-menu-app': true, 'active': activeApp === appId}"
v-bind:icon="app.icon"
v-bind:label="app.name"
v-bind:href="app.href"
v-bind:target="targetBlankApps.indexOf(appId) !== -1 ? '_blank' : undefined"
/>
</ul>
</div>
</div>
</div>
</div>
</template>
<script>
import trim from 'trim'
import axios from 'axios'
import OpenerButton from './OpenerButton'
import CloserButton from './CloserButton'
import SettingsButton from './SettingsButton'
import Loader from './Loader'
import SideMenuBigApp from './SideMenuBigApp'
export default {
name: 'SideMenuWithCategories',
components: {
SettingsButton,
OpenerButton,
CloserButton,
Loader,
SideMenuBigApp,
},
data() {
return {
items: [],
activeApp: null,
targetBlank: false,
targetBlankApps: [],
settings: null,
}
},
methods: {
retrieveApps() {
this.apps = []
let that = this
axios
.get(OC.generateUrl('/apps/side_menu/nav/items'))
.then(function(response) {
that.items = response.data.items
let apps = []
for (let category of that.items) {
for (let a in category.apps) {
apps.push(category.apps[a])
}
}
document.querySelector('body').dispatchEvent(new CustomEvent('side-menu.apps', {
detail: {apps: apps},
}))
})
},
retrieveActiveApp() {
let activeAppLink = document.querySelector('#appmenu a.active')
this.activeApp = activeAppLink ? activeAppLink.parentNode.getAttribute('data-id') : null
},
retrieveConfig() {
let that = this
axios
.get(OC.generateUrl('/apps/side_menu/js/config'))
.then(function(response) {
const config = response.data
that.targetBlankApps = config['target-blank-apps']
that.settings = config['settings']
})
},
},
mounted() {
this.retrieveConfig()
this.retrieveApps()
this.retrieveActiveApp()
}
}
</script>
+179 -51
View File
@@ -15,15 +15,32 @@
* 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 AdminCategoriesCustom from './AdminCategoriesCustom.vue'
import Vue from 'vue'
Vue.prototype.OC = window.OC
Vue.prototype.OCA = window.OCA
let elements = [] let elements = []
const selector = '#side-menu-message'; const selector = '#side-menu-message'
const userConfig = (name, value, callbacks) => { const userConfig = (name, value, callbacks) => {
const url = OC.generateUrl('/apps/side_menu/personalSetting/valueSet') const url = OC.generateUrl('/apps/side_menu/personalSetting/valueSet')
const formData = []
$.post(url, {name: name, value: value}, callbacks.success) formData.push('name=' + encodeURIComponent(name))
.fail(callbacks.error) 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) => { const appConfig = (name, value, callbacks) => {
@@ -31,23 +48,29 @@ const appConfig = (name, value, callbacks) => {
} }
const saveSettings = (key) => { const saveSettings = (key) => {
const element = elements.get(key) const element = elements[key]
if (!element) {
return
}
let value let value
let name let name
if (jQuery(element).is('[data-checkbox]')) { if (element.hasAttribute('data-checkbox')) {
name = jQuery(element).attr('data-name') name = element.getAttribute('data-name')
const inputs = jQuery('input[name="' + name + '[]"]:checked')
value = [] value = []
inputs.each((i, v) => { const inputs = document.querySelectorAll('input[name="' + name + '[]"]:checked')
value.push(v.value)
}) for (let input of inputs) {
value.push(input.value)
}
value = JSON.stringify(value) value = JSON.stringify(value)
} else { } else {
name = jQuery(element).attr('name') name = element.getAttribute('name')
value = jQuery(element).val() value = element.value
} }
const size = elements.length const size = elements.length
@@ -56,17 +79,21 @@ const saveSettings = (key) => {
++value ++value
} }
const progress = document.querySelector('#side-menu-save-progress')
progress.style.width = '40px';
progress.style.marginLeft = '5px';
const callbacks = { const callbacks = {
success: () => { success: () => {
OC.msg.finishedSuccess( const percent = parseInt((key + 1) * 100 / size);
selector,
t('side_menu', (key + 1) + '/' + size) progress.setAttribute('value', percent)
)
if (key < size - 1) { if (key < size - 1) {
saveSettings(++key) saveSettings(key + 1)
} else { } else {
OC.msg.finishedSuccess(selector, t('side_menu', 'Saved')) location.reload()
} }
}, },
error: () => { error: () => {
@@ -74,7 +101,7 @@ const saveSettings = (key) => {
} }
} }
if (jQuery(element).is('[data-personal]')) { if (element.hasAttribute('data-personal')) {
userConfig(name, value, callbacks) userConfig(name, value, callbacks)
} else { } else {
appConfig(name, value, callbacks) appConfig(name, value, callbacks)
@@ -82,51 +109,152 @@ const saveSettings = (key) => {
} }
const elementToggler = (element) => { const elementToggler = (element) => {
jQuery(element).toggle() let display = 'none'
if (window.getComputedStyle(element).display === 'none') {
display = 'block'
}
element.style.display = display
} }
jQuery(document).ready(() => { const updateAppsCategoriesCustom = () => {
elements = jQuery('.side-menu-setting') let values = {}
jQuery('#side-menu-save').on('click', (event) => { 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 View = Vue.extend(AdminCategoriesCustom)
const adminCategoriesCustom = new View({})
adminCategoriesCustom.$mount('#side-menu-categories-custom')
}
elements = document.querySelectorAll('.side-menu-setting')
document.querySelector('#side-menu-save').addEventListener('click', (event) => {
event.preventDefault() event.preventDefault()
OC.msg.startSaving(selector) OC.msg.startSaving(selector)
saveSettings(0) 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-setting-live').on('change', (event) => { const resets = document.querySelectorAll('.btn-reset')
var target = jQuery(event.target)
var name = target.attr('name')
var value = target.val()
if (name === 'opener') { for (let btn of resets) {
var url = OC.generateUrl(`/apps/side_menu/img/${value}.svg`).replace('/index.php', '') btn.addEventListener('click', (event) => {
const target = event.target
const values = JSON.parse(target.getAttribute('data-reset'))
value = `url(${url})`; for (let i in values) {
} document.querySelector(`#${i}`).value = values[i]
}
})
}
if (name === 'icon-invert-filter' || name === 'icon-opacity') { const displays = document.querySelectorAll('.side-menu-display')
value/=100;
}
document.documentElement.style.setProperty('--side-menu-' + name, value) 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'
}) })
jQuery('.side-menu-toggler').on('click', (event) => { try {
var target = jQuery(event.target) sortable('#categories-list .side-menu-setting-list')[0].addEventListener('sortstop', (e) => {
var element = target.attr('data-target') let value = []
elementToggler(element) 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) {
}
})
+93
View File
@@ -0,0 +1,93 @@
"Custom menu": "Uživatelsky určená nabídka"
"Enable the custom menu": "Zapnout uživatelsky určenou nabídku"
"No": "Ne"
"Yes": "Ano"
"Menu": "Nabídka"
'Use the shortcut <span class="keyboard-key">Ctrl</span>+<span class="keyboard-key">o</span> to open and to hide the side menu. Use <span class="keyboard-key">tab</span> to navigate.': 'Pro otevření/skrytí postranní nabídky použijte zkratku <span class="keyboard-key">Ctrl</span>+<span class="keyboard-key">o</span>. Pro pohyb po použijte klávesu <span class="keyboard-key">tab</span>.'
"Top menu": "Hlavní nabídka"
"Apps that not must be moved in the side menu": "Aplikace, které nepřesouvat do postranní nabídky"
"If there is no selection then the global configuration is applied.": "Pokud neexistuje žádný výběr, je uplatněno globální nastavení."
"Experimental": "Experimentální"
"Save": "Uložit"
"You like this app and you want to support me?": "Líbí se vám tato aplikace a chcete podpořit její vývoj?"
"Buy me a coffee ☕": "Kupte mi kafe ☕"
"Hidden": "Skryté"
"Small": "Malé"
"Normal": "Normální"
"Big": "Velké"
"Colors": "Barvy"
"Background color": "Barva pozadí"
"Background color of current app": "Barva pozadí stávající aplikace"
"Text color": "Barva textu"
"Loader": "Nástroj pro načítání"
"Icon": "Ikona"
"Same color": "Stejná barva"
"Opposite color": "Doplňková barva"
"Transparent": "Průhledné"
"Opaque": "Neprůhledné"
"Opener": "Tlačítko pro otevření"
"Default": "Výchozí"
"Default (dark)": "Výchozí (tmavé)"
"Hamburger": "Hamburger"
"Hamburger (dark)": "Hamburger (tmavé)"
"Hamburger 2": "Hamburger 2"
"Hamburger 2 (dark)": "Hamburger 2 (tmavé)"
"Before the logo": "Před logem"
"After the logo": "Za logem"
"Position": "Pozice"
"Show only the opener (hidden logo)": "Zobrazovat pouze otevírací tlačítko (logo skryto)"
"Do not display the side menu and the opener if there is no application (eg: public pages).": "Nezobrazovat postranní nabídku a její otevírací tlačítko pokud nejsou dostupné žádné aplikace (např. na veřejných stránkách)."
"Panel": "Panel"
"Open the menu when the mouse is hover the opener (automatically disabled on touch screens)": "Otevřít nabídku při najetím ukazatelem na tlačítko nabídky (automaticky vypnuto pro dotykové obrazovky)."
"Display the big menu": "Zobrazit velkou nabídku"
"This menu is not compatible with AppOrder.": "Nabídka není kompatibilní s jinou aplikací (doplňkem) „Pořadí aplikací“."
"Display the logo": "Zobrazit logo"
"This feature is not compatible with the <code>big menu</code> display.": "Tato funkce není kompatibilní se zobrazením <code>velké nabídky</code>."
"Icons and texts": "Ikony a texty"
"Loader enabled": "Načítání zapnuto"
"Tips": "Tipy"
"Always displayed": "Vždy zobrazeno"
"The logo will be hidden when the menu is always displayed.": "Pokud je nabídka zobrazena trvale, logo bude skryto."
"This is the automatic behavior when the menu is always displayed.": "Toto je automatické chování, kdy je nabídka vždy zobrazena."
"Not compatible with touch screens.": "Nekompatibilní s dotykovými obrazovkami."
"Big menu": "Velká nabídka"
"Live preview": "Živý náhled"
"Open apps in new tab": "Otevírat aplikace v novém panelu"
"Use the global setting": "Použít globální nastavení"
"Use my selection": "Použít můj výběr"
"Show and hide the list of applications": "Zobrazit/skrýt seznam aplikací"
"Use the avatar instead of the logo": "Použít namísto loga profilový obrázek uživatele"
"You do not have permission to change the settings.": "Nemáte oprávnění měnit nastavení."
"Force this configuration to users": "Vynutit uplatnění těchto nastavení uživatelům"
"Export the configuration": "Exportovat nastavení"
"Purge the cache": "Vyprázdnit mezipaměť"
"Show the link to settings": "Zobrazit odkaz na nastavení"
"The menu is enabled by default for users": "Nabídka je ve výchozím stavu zapnutá pro uživatele"
"Except when the configuration is forced.": "S výjimkou, kdy je nastavení vynuceno."
"Apps that should not be displayed in the menu": "Aplikace, které by neměly být v nabídce zobrazeny"
"This feature is only compatible with the <code>big menu</code> display.": "Tato funkce je kompatibilní pouze s <code>velkou nabídkou</ code>."
"The logo is a link to the default app": "Logo je odkaz na výchozí aplikaci"
"Others": "Ostatní"
"Categories": "Kategorie"
"Customize sorting": "Přizpůsobit si řazení"
"Order by": "Řadit podle"
"Name": "Název"
"Customed": "Přizpůsobeno"
"Show and hide the list of categories": "Zobrazit/skrýt seznam kategorií"
"This parameters are used when Dark theme or Breeze Dark Theme are enabled.": "Tyto parametry jsou použity v případě, že je zapnutý (Breeze) tmavý motiv vzhledu."
"Dark mode colors": "Barvy tmavého režimu"
"With categories": "S kategoriemi"
"Custom categories": "Vlastní kategorie"
"Customize application categories": "Personnaliser les catégories des applications"
"Customize application categories": "Přizpůsobte kategorie aplikací"
"Apps only visible in the top menu": "Aplikace jsou viditelné pouze v horní nabídce "
"Apps visible in the top and side menus": "Aplikace viditelné v horní a boční nabídce"
"Reset to default": "Vrátit zpět na výchozí hodnoty"
"Hidden icon": "Skrytá ikona"
"Small icon": "Malá ikona"
"Normal icon": "Normální ikona"
"Big icon": "Velká ikona"
"Hidden text": "Skrytý text"
"Small text": "Malý text"
"Normal text": "Normální text"
"Big text": "Velký text"
+51 -28
View File
@@ -1,31 +1,31 @@
"Custom menu": "Benutzerdefiniertes Menü" "Custom menu": "Benutzerdefiniertes Menü"
"Enable the custom menu": "Aktiviere das Benutzerdefiniertes Menü" "Enable the custom menu": "Benutzerdefiniertes Menü aktivieren"
"No": "Nein" "No": "Nein"
"Yes": "Ja" "Yes": "Ja"
"Menu": "Menü" "Menu": "Menü"
'Use the shortcut <span class="keyboard-key">Ctrl</span>+<span class="keyboard-key">o</span> to open and to hide the side menu. Use <span class="keyboard-key">tab</span> to navigate.': 'Verwende die Tastenkombination <span class="keyboard-key">Strg</span>+<span class="keyboard-key">o</span>, um das Seitenmenü ein- und auszublenden. Verwende <span class="keyboard-key">tab</span> zum Navigieren.' 'Use the shortcut <span class="keyboard-key">Ctrl</span>+<span class="keyboard-key">o</span> to open and to hide the side menu. Use <span class="keyboard-key">tab</span> to navigate.': 'Verwende die Tastenkombination <span class="keyboard-key">Strg</span>+<span class="keyboard-key">o</span>, um das Seitenmenü ein- und auszublenden. Verwende <span class="keyboard-key">tab</span> zum Navigieren.'
"Top menu": "Hauptmenü" "Top menu": "Obere Navigationsleiste"
"Apps that not must be moved in the side menu": "Apps, die nicht ins Seitenmenü verschoben werden müssen" "Apps that not must be moved in the side menu": "Anwendungen, die nicht ins Seitenmenü verschoben werden sollen"
"If there is no selection then the global configuration is applied.": "Wenn keine Auswahl vorhanden ist, wird die globale Konfiguration angewendet." "If there is no selection then the global configuration is applied.": "Wenn keine Auswahl vorhanden ist, wird die globale Konfiguration angewendet."
"Experimental": "Experimentell" "Experimental": "Experimentell"
"Save": "Speichern" "Save": "Speichern"
"You like this app and you want to support me?": "Du magst diese App und möchtest mich unterstützen?" "You like this app and you want to support me?": "Du magst diese Anwendung und möchtest mich unterstützen?"
"Buy me a coffee ☕": "Gib mir einen Kaffee ☕" "Buy me a coffee ☕": "Gib mir einen Kaffee aus ☕"
"Hidden": "Versteckt" "Hidden": "Ausblenden"
"Small": "Klein" "Small": "Klein"
"Normal": "Normal" "Normal": "Normal"
"Big": "Groß" "Big": "Groß"
"Colors": "Farben" "Colors": "Farben"
"Background color": "Hintergrundfarbe" "Background color": "Hintergrundfarbe"
"Background color of current app": "Hintergrundfarbe der aktuellen App" "Background color of current app": "Hintergrundfarbe der aktuellen Anwendung"
"Text color": "Textfarbe" "Text color": "Textfarbe"
"Loader": "Ladestandanzeige" "Loader": "Ladestandanzeige"
"Icon": "Symbol" "Icon": "Symbol"
"Same color": "Selbe Farbe" "Same color": "Selbe Farbe"
"Opposite color": "Gegenfarbe" "Opposite color": "Gegenfarbe"
"Transparent": "Transparent" "Transparent": "Transparent"
"Opaque": "Undurchsichtig" "Opaque": "Nicht transparent"
"Opener": "Öffner" "Opener": "Menü-Symbol"
"Default": "Standard" "Default": "Standard"
"Default (dark)": "Standard (dunkel)" "Default (dark)": "Standard (dunkel)"
"Hamburger": "Hamburger" "Hamburger": "Hamburger"
@@ -35,35 +35,58 @@
"Before the logo": "Vor dem Logo" "Before the logo": "Vor dem Logo"
"After the logo": "Nach dem Logo" "After the logo": "Nach dem Logo"
"Position": "Position" "Position": "Position"
"Show only the opener (hidden logo)": "Nur den Öffner anzeigen (verstecktes Logo)" "Show only the opener (hidden logo)": "Nur das Menü-Symbol anzeigen (Logo wird ausgeblendet)"
"Do not display the side menu and the opener if there is no application (eg: public pages).": "Zeige das Seitenmenü und den Öffner nicht an, wenn keine Anwendung vorhanden ist (z.B. bei öffentlichen Seiten)." "Do not display the side menu and the opener if there is no application (eg: public pages).": "Zeige das Seitenmenü und das Menü-Symbol nicht an, wenn keine Anwendung vorhanden ist (z.B. bei öffentlichen Seiten)."
"Panel": "Panel" "Panel": "Panel"
"Open the menu when the mouse is hover the opener (automatically disabled on touch screens)": "Öffne das Menü, wenn die Maus über den Öffner bewegt wird (auf Touchscreens automatisch deaktiviert)." "Open the menu when the mouse is hover the opener (automatically disabled on touch screens)": "Öffne das Menü, wenn die Maus über das Menü-Symbol bewegt wird (auf Touchscreens automatisch deaktiviert)."
"Display the big menu": "Großes Menü anzeigen" "Display the big menu": "Großes Menü anzeigen"
"The big menu is not compatible with AppOrder.": "Das große Menü ist nicht mit AppOrder kompatibel." "This menu is not compatible with AppOrder.": "Dieses Menü ist nicht mit <code>AppOrder</code> kompatibel."
"Display the logo": "Logo anzeigen" "Display the logo": "Logo anzeigen"
"This feature is not compatible with the <code>big menu</code> display.": "Diese Funktion ist nicht mit <code>großes Menü</code> kompatibel." "This feature is not compatible with the <code>big menu</code> display.": "Diese Funktion ist nicht mit dem <code>großen Menü</code> kompatibel."
"Icons and texts": "Symbole und Texte" "Icons and texts": "Symbole und Texte"
"Loader enabled": "Loader aktiviert" "Loader enabled": "Ladestandanzeige aktiviert"
"Tips": "Tipps" "Tips": "Tipps"
"Always displayed": "Wird immer angezeigt" "Always displayed": "Immer anzeigen"
"The logo will be hidden when the menu is always displayed.": "Das Logo wird ausgeblendet, wenn das Menü immer angezeigt wird." "The logo will be hidden when the menu is always displayed.": "Das Logo wird ausgeblendet, wenn das Menü immer angezeigt wird."
"This is the automatic behavior when the menu is always displayed.": "Dies ist das automatische Verhalten, wenn das Menü immer angezeigt wird." "This is the automatic behavior when the menu is always displayed.": "Dies ist das automatische Verhalten, wenn das Menü immer angezeigt wird."
"Not compatible with touch screens.": "Nicht kompatibel mit Touchscreens." "Not compatible with touch screens.": "Nicht kompatibel mit Touchscreens."
"Big menu": "Großes Menü" "Big menu": "Großes Menü"
"Live preview": "Live Vorschau" "Live preview": "Live-Vorschau"
"Open apps in new tab": "Öffne Apps in einem neuen Tab" "Open apps in new tab": "Öffne Anwendungen in einem neuen Tab"
"Use the global setting": "Verwende die globale Einstellung" "Use the global setting": "Verwende die globale Einstellung"
"Use my selection": "Verwende meine Auswahl" "Use my selection": "Verwende meine Auswahl"
"Show and hide the list of applications": "Ein- und Ausblenden der Anwendungsliste" "Show and hide the list of applications": "Ein- und Ausblenden der Anwendungsliste"
"Use the avatar instead of the logo": "Verwenden Sie den Avatar anstelle des Logos" "Use the avatar instead of the logo": "Avatar anstelle des Logos anzeigen"
"You do not have permission to change the settings.": "Sie haben keine Berechtigung zum Ändern der Einstellungen." "You do not have permission to change the settings.": "Du hast keine Berechtigung, die Einstellungen dieser Anwendung zu ändern."
"Force this configuration to users": "Erzwingen Sie diese Konfiguration für Benutzer" "Force this configuration to users": "Konfiguration für alle Benutzer erzwingen"
"Export the configuration": "Exportieren Sie die Konfiguration" "Export the configuration": "Konfiguration exportieren"
"Purge the cache": "Leeren Sie den Cache" "Purge the cache": "Cache leeren"
"Show the link to settings": "Zeigen Sie den Link zu den Einstellungen an" "Show the link to settings": "Link zu den Einstellungen anzeigen"
"The menu is enabled by default for users": "Das Menü ist standardmäßig für Benutzer aktiviert" "The menu is enabled by default for users": "Das Menü ist standardmäßig für alle Benutzer aktiviert"
"Except when the configuration is forced.": "Außer wenn die Konfiguration erzwungen wird." "Except when the configuration is forced.": "Gilt nicht, wenn die Konfiguration erzwungen wird."
"Apps that should not be displayed in the menu": "Apps, die nicht im Menü angezeigt werden sollen" "Apps that should not be displayed in the menu": "Anwendungen, die nicht im Menü angezeigt werden sollen"
"This feature is only compatible with the <code>big menu</code> display.": "Kompatibel mit der Anzeige <code> Großes Menü </ code>." "This feature is only compatible with the <code>big menu</code> display.": "Kompatibel mit dem <code>großen Menü</code>."
"The logo is a link to the default app": "Das Logo ist ein Link zur Standard-App" "The logo is a link to the default app": "Das Logo ist ein Link zur Standard-App"
"Others": "Andere"
"Categories": "Kategorien"
"Customize sorting": "Sortierung anpassen"
"Order by": "Sortieren nach"
"Name": "Name"
"Customed": "Benutzerdefiniert"
"Show and hide the list of categories": "Liste der Kategorien ein- und ausblenden"
"This parameters are used when Dark theme or Breeze Dark Theme are enabled.": "Diese Optionen werden auf <code>Dark Theme</code> oder <code>Breeze Dark Theme</code> angewendet."
"Dark mode colors": "Farben für den dunklen Modus"
"With categories": "Mit Kategorien"
"Custom categories": "Benutzerdefinierte Kategorien"
"Customize application categories": "Anwendungskategorien anpassen"
"Apps only visible in the top menu": "Apps nur im oberen Menü sichtbar "
"Apps visible in the top and side menus": "Apps im oberen und seitlichen Menü sichtbar"
"Reset to default": "Auf Standard zurücksetzen"
"Hidden icon": "Verstecktes Symbol"
"Small icon": "Kleines Symbol"
"Normal icon": "Normales Symbol"
"Big icon": "Große Ikone"
"Hidden text": "Versteckter Text"
"Small text": "Kleiner Text"
"Normal text": "Normaler Text"
"Big text": "Großer Text"
+24 -1
View File
@@ -15,6 +15,14 @@
"Small": "Petit" "Small": "Petit"
"Normal": "Normal" "Normal": "Normal"
"Big": "Gros" "Big": "Gros"
"Hidden icon": "Icône masqué"
"Small icon": "Petit icône"
"Normal icon": "Icône normal"
"Big icon": "Gros icône"
"Hidden text": "Text masqué"
"Small text": "Texte petit"
"Normal text": "Texte normal"
"Big text": "Gros texte"
"Colors": "Couleurs" "Colors": "Couleurs"
"Background color": "Couleur de fond" "Background color": "Couleur de fond"
"Background color of current app": "Couleur de fond de l'application en cours" "Background color of current app": "Couleur de fond de l'application en cours"
@@ -40,7 +48,7 @@
"Panel": "Panneau" "Panel": "Panneau"
"Open the menu when the mouse is hover the opener (automatically disabled on touch screens)": "Ouvrir le menu au passage de la souris (automatiquement désactivé sur les écrans tactiles)" "Open the menu when the mouse is hover the opener (automatically disabled on touch screens)": "Ouvrir le menu au passage de la souris (automatiquement désactivé sur les écrans tactiles)"
"Display the big menu": "Afficher le menu large" "Display the big menu": "Afficher le menu large"
"The big menu is not compatible with AppOrder.": "Le menu large n'est pas compatible avec l'application AppOrder" "This menu is not compatible with AppOrder.": "Ce menu n'est pas compatible avec l'application AppOrder"
"Display the logo": "Afficher le logo" "Display the logo": "Afficher le logo"
"This feature is not compatible with the <code>big menu</code> display.": "Cette fonctionnalité n'est pas compatible avec l'affichage du menu large." "This feature is not compatible with the <code>big menu</code> display.": "Cette fonctionnalité n'est pas compatible avec l'affichage du menu large."
"Icons and texts": "Icônes et textes" "Icons and texts": "Icônes et textes"
@@ -67,3 +75,18 @@
"Apps that should not be displayed in the menu": "Applications qui ne doivent pas être affichées dans le menu" "Apps that should not be displayed in the menu": "Applications qui ne doivent pas être affichées dans le menu"
"This feature is only compatible with the <code>big menu</code> display.": "Compatible avec l'affichage <code>Menu large</code>." "This feature is only compatible with the <code>big menu</code> display.": "Compatible avec l'affichage <code>Menu large</code>."
"The logo is a link to the default app": "Le logo est un lien vers l'application par défaut" "The logo is a link to the default app": "Le logo est un lien vers l'application par défaut"
"Others": "Autres"
"Categories": "Catégories"
"Customize sorting": "Personnaliser le tri"
"Order by": "Trier par"
"Name": "Nom"
"Customed": "Personnalisé"
"Show and hide the list of categories": "Afficher et masquer la liste des catégories"
"This parameters are used when Dark theme or Breeze Dark Theme are enabled.": "Ces paramètres sont utilisés lorsque le thème sombre ou le thème Breeze Dark sont activés."
"Dark mode colors": "Couleurs du mode sombre"
"With categories": "Avec les catégories"
"Custom categories": "Catégories personnalisées"
"Customize application categories": "Personnaliser les catégories des applications"
"Apps only visible in the top menu": "Applications visibles uniquement dans le menu supérieur"
"Apps visible in the top and side menus": "Applications visibles dans le menus supérieur et latéral"
"Reset to default": "Restaurer les valeurs par défaut"
+92
View File
@@ -0,0 +1,92 @@
"Custom menu": "自定义菜单"
"Enable the custom menu": "激活自定义菜单"
"No": "取消"
"Yes": "确定"
"Menu": "菜单"
"Use the shortcut <span class=\"keyboard-key\">Ctrl<\/span>+<span class=\"keyboard-key\">o<\/span> to open and to hide the side menu. Use <span class=\"keyboard-key\">tab<\/span> to navigate.": "使用快捷键 <span class=\"keyboard-key\">Ctrl<\/span>+<span class=\"keyboard-key\">o<\/span> 打开或隐藏侧边栏菜单。使用<span class=\"keyboard-key\">tab<\/span> 来导航。"
"Top menu": "顶部菜单"
"Apps that not must be moved in the side menu": "禁止在侧边栏菜单移动的应用"
"If there is no selection then the global configuration is applied.": "如不选择,将应用全局设定。"
"Experimental": "实验性"
"Save": "保存"
"You like this app and you want to support me?": "喜欢本应用并支持我一下?"
"Buy me a coffee ☕": "赏一杯咖啡 ☕ 给我"
"Hidden": "隐藏"
"Small": "小型"
"Normal": "标准"
"Big": "大型"
"Colors": "颜色"
"Background color": "背景颜色"
"Background color of current app": "当前应用的背景色"
"Text color": "文字颜色"
"Loader": "菜单指示器"
"Icon": "图标"
"Same color": "相同颜色"
"Opposite color": "相反颜色"
"Transparent": "透明"
"Opaque": "不透明"
"Opener": "容器"
"Default": "默认"
"Default (dark)": "默认(深色)"
"Hamburger": "Hamburger"
"Hamburger (dark)": "Hamburger (深色)"
"Hamburger 2": "Hamburger 2"
"Hamburger 2 (dark)": "Hamburger 2 (深色)"
"Before the logo": "在logo前"
"After the logo": "在logo后"
"Position": "位置"
"Show only the opener (hidden logo)": "只显示容器 (隐藏logo)"
"Do not display the side menu and the opener if there is no application (eg: public pages).": "N如果没有应用,不显示侧边栏菜单和容器 (例如 : 公共页面)。"
"Panel": "面板"
"Open the menu when the mouse is hover the opener (automatically disabled on touch screens)": "鼠标悬停时打开菜单 (触摸屏时将自动禁用)"
"Display the big menu": "显示大型菜单"
"This menu is not compatible with AppOrder.": "型菜单与应用顺序不兼容"
"Display the logo": "显示logo"
"This feature is not compatible with the <code>big menu<\/code> display.": "此功能与显示<code>大型菜单<\/code>不兼容。"
"Icons and texts": "图标与文字"
"Loader enabled": "菜单指示器已激活"
"Tips": "技巧"
"Always displayed": "一直显示"
"The logo will be hidden when the menu is always displayed.": "一直显示菜单时logo将被隐藏。"
"This is the automatic behavior when the menu is always displayed.": "一直显示菜单时的自动动作。"
"Not compatible with touch screens.": "与触屏不兼容。"
"Big menu": "大型菜单"
"Live preview": "实时预览"
"Open apps in new tab": "在新标签中打开应用"
"Use the global setting": "使用全局设定"
"Use my selection": "使用自定义设定"
"Show and hide the list of applications": "显示或隐藏应用列表"
"Use the avatar instead of the logo": "使用头像代替logo"
"You do not have permission to change the settings.": "没有更改设置的权限。"
"Force this configuration to users": "强制用户使用此设置"
"Export the configuration": "导出设置"
"Purge the cache": "清除缓存"
"Show the link to settings": "显示设置链接"
"The menu is enabled by default for users": "用户的默认菜单已激活"
"Except when the configuration is forced.": "除非设置被强制使用。"
"Apps that should not be displayed in the menu": "禁止在菜单中显示的应用"
"This feature is only compatible with the <code>big menu<\/code> display.": "此功能只和<code>大型菜单<\/code>兼容。"
"The logo is a link to the default app": "logo链接到默认应用"
"Others": "其他"
"Categories": "类别"
"Customize sorting": "自定义顺序"
"Order by": "排序规则"
"Name": "名称"
"Customed": "自定义"
"Show and hide the list of categories": "显示或隐藏类别列表"
"This parameters are used when Dark theme or Breeze Dark Theme are enabled.": "此参数将应用于暗黑主题激活时。"
"Dark mode colors": "暗黑模式颜色"
"With categories": "有类别"
"Custom categories": "自定义类别"
"Customize application categories": "自定义应用程序类别"
"Apps only visible in the top menu": "应用程序仅在顶部菜单中可见"
"Apps visible in the top and side menus": "顶部和侧边菜单中可见的应用程序"
"Reset to default": "重置为默认设置"
"Hidden icon": "隐藏图标"
"Small icon": "小图标"
"Normal icon": "正常图标"
"Big icon": "大图标"
"Hidden text": "隐藏文字"
"Small text": "小文本"
"Normal text": "普通文本"
"Big text": "大文本"
+1 -1
View File
@@ -8,7 +8,7 @@
<?php endforeach; ?> <?php endforeach; ?>
} }
<?php if (empty($_['top-menu-apps'])): ?> <?php if (empty($_['top-menu-apps']) && empty($_['top-side-menu-apps'])): ?>
#appmenu { #appmenu {
display: none; display: none;
} }
+29 -17
View File
@@ -1,21 +1,19 @@
var alwaysDisplayed = function() { const alwaysDisplayed = function() {
var elements = document.querySelectorAll('*'); const elements = querySelectorAll('*')
var fixedElements = [] const fixedElements = []
for (var i in elements) {
var element = elements[i]
for (var element of elements) {
if (typeof element !== 'object') { if (typeof element !== 'object') {
continue continue
} }
var position = window.getComputedStyle(element, null).getPropertyValue('position'); const position = window.getComputedStyle(element, null).getPropertyValue('position')
if (position !== 'fixed') { if (position !== 'fixed') {
continue continue
} }
var id = element.getAttribute('id') const id = element.getAttribute('id')
if (id === 'header' || id === 'side-menu' || id === 'side-menu-loader') { if (id === 'header' || id === 'side-menu' || id === 'side-menu-loader') {
continue continue
@@ -25,7 +23,21 @@ var alwaysDisplayed = function() {
continue 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 continue
} }
@@ -33,19 +45,19 @@ var alwaysDisplayed = function() {
} }
for (var i in fixedElements) { for (var i in fixedElements) {
var element = fixedElements[i] const element = fixedElements[i]
var computedStyle = window.getComputedStyle(element, null) const computedStyle = window.getComputedStyle(element, null)
var left = computedStyle.getPropertyValue('left') const left = computedStyle.getPropertyValue('left')
var right = computedStyle.getPropertyValue('right') const right = computedStyle.getPropertyValue('right')
if (right !== '0px') { if (right !== '0px') {
var intValue = parseInt(left.replace('px', '')) const intValue = parseInt(left.replace('px', '')) + 50
element.style.setProperty('transform', 'translateX(' + (intValue + 50) + 'px)') element.style.setProperty('transform', 'translateX(' + intValue.toString() + 'px)')
} }
} }
} }
let content = document.getElementById('content') const content = querySelector('#content')
if (content && content.classList.contains('app-settings')) { if (content && content.classList.contains('app-settings')) {
let loaded = false let loaded = false
@@ -56,7 +68,7 @@ if (content && content.classList.contains('app-settings')) {
} }
const observer = new MutationObserver(() => { const observer = new MutationObserver(() => {
if (loaded) { if (loaded) {
return; return
} }
const element = content.querySelector('#app-category-your-apps') || content.querySelector('#app-navigation ul') const element = content.querySelector('#app-category-your-apps') || content.querySelector('#app-navigation ul')
+8 -9
View File
@@ -1,15 +1,14 @@
var pageLoader = jQuery('<div id="side-menu-loader">') let pageLoader = createElement('div', {id: 'side-menu-loader'})
var pageLoaderBar = jQuery('<div id="side-menu-loader-bar">') let pageLoaderBar = createElement('div', {id: 'side-menu-loader-bar'})
body.append(pageLoader) pageLoader.appendChild(pageLoaderBar)
pageLoader.append(pageLoaderBar) querySelector('body').appendChild(pageLoader)
var pageLoaderValue = 0 let pageLoaderValue = 0
$(window).on('beforeunload', function() {
setInterval(function() {
pageLoaderBar.width(pageLoaderValue.toString() + '%')
window.addEventListener('beforeunload', () => {
setInterval(() => {
pageLoaderBar.style.width = pageLoaderValue.toString() + '%'
pageLoaderValue = Math.min(pageLoaderValue + .2, 100) pageLoaderValue = Math.min(pageLoaderValue + .2, 100)
}, 25) }, 25)
}) })
+144 -100
View File
@@ -1,60 +1,91 @@
var menuCache = null let menuCache = null
var updateTopMenu = function() { const breakpointMobileWidth = 1024
var breakpointMobileWidth = 1024 const usePercentualAppMenuLimit = 0.8
var menu = jQuery('#appmenu') const minAppsDesktop = 8
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
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 return
} }
navigationApps.html('') navigationAppsHtml = ''
apps.each(function(i, app) { for (let app of apps) {
var dataId = app.getAttribute('data-id') const dataId = app.getAttribute('data-id')
if (dataId === null) { if (dataId === null) {
return continue
} }
if (topMenuApps.indexOf(dataId) === -1) { if (topMenuApps.indexOf(dataId) === -1 && topSideMenuApps.indexOf(dataId) === -1) {
app.classList.add('hidden') app.classList.add('hidden')
app.classList.add('app-hidden') app.classList.add('app-hidden')
} else { } else {
app.classList.remove('hidden') app.classList.remove('hidden')
app.classList.add('app-external-site') app.classList.add('app-external-site')
if (topSideMenuApps.indexOf(dataId) !== -1) {
app.classList.add('app-top-side-menu')
}
appShown.push(app) appShown.push(app)
navigationApps.append(app.outerHTML)
navigationAppsHtml = navigationAppsHtml + app.outerHTML
} }
if (targetBlankApps.indexOf(dataId) !== -1) { 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) { if (isMobile && appCount > minAppsDesktop) {
appCount = minAppsDesktop appCount = minAppsDesktop
@@ -62,111 +93,124 @@ var updateTopMenu = function() {
appCount = minAppsDesktop appCount = minAppsDesktop
} }
if (appCount === 0) { menu.style.opacity = 1
menu.addClass('hidden')
}
menu.removeClass('hidden')
menu.css('opacity', 1)
if (appShown.length - 1 - appCount >= 1) { if (appShown.length - 1 - appCount >= 1) {
appCount-- appCount--
} }
moreApps.find('a').removeClass('active') for (let item of querySelectorAll('a', moreApps)) {
item.classList.remove('active')
}
var k = 0 let k = 0
var notInHeader = 0 let notInHeader = 0
var name
jQuery(appShown).each(function(i, app) { for (let app of appShown) {
app = jQuery(app) const name = app.getAttribute('data-id')
name = app.data('id') const li = querySelector('#apps li[data-id=' + name + '].app-external-site')
if (k < appCount && appCount > 0) { if (k < appCount && appCount > 0) {
app.removeClass('hidden') app.classList.remove('hidden')
lastShownApp = app li.classList.add('in-header')
jQuery('#apps li[data-id=' + name + '].app-external-site').addClass('in-header') lastShownApp = app
} else { } else {
app.addClass('hidden') app.classList.add('hidden')
li.classList.remove('in-header')
notInHeader++ 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')) { if (appCount > 0 && a.classList.contains('active')) {
lastShownApp.addClass('hidden') lastShownApp.classList.add('hidden')
app.removeClass('hidden') app.classList.remove('hidden')
notInHeader++ notInHeader++
jQuery('#apps li[data-id=' + name + '].app-external-site') li.classList.add('in-header')
.removeClass('in-header')
.addClass('in-header')
} }
} }
k++ k++
}) }
// Hack for https://github.com/nextcloud/server/blob/23b0b63c213f5b31eecae817ffd4a9e26f6624d0/core/src/components/MainMenu.js#L74-L96 // Hack for:
menu.undelegate('li:not(#more-apps) > a', 'click') // - https://github.com/nextcloud/server/blob/master/core/src/components/MainMenu.js#L97-L119
menu.delegate('li:not(#more-apps) > a', 'click', function(e) { // - https://github.com/nextcloud/server/blob/master/core/src/components/MainMenu.js#L97-L119
var a = $(e.target) jQuery(menu).undelegate('li:not(#more-apps) > a', 'click')
jQuery(navigation).undelegate('a', 'click')
if (!a.is('a')) { const confs = [
a = a.closest('a') {
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) { for (let app of querySelectorAll('#apps li.app-external-site')) {
a.find('svg').remove() const appId = app.getAttribute('data-id')
a.find('div').remove()
a.prepend(jQuery('<div/>').addClass(
OCA.Theming && OCA.Theming.inverted
? 'icon-loading-small'
: 'icon-loading-small-dark'
))
window.location.href = a.attr('href') if (app.classList.contains('in-header')) {
} for (let defs of querySelectorAll('svg defs', app)) {
}) defs.remove()
}
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()
} else { } else {
var svg = app.find('svg'); const svg = querySelector('svg', app)
if (svg.find('defs').length > 0) { if (querySelectorAll('svg defs', app).length > 0) {
return; continue
} }
var defs = ` const defs = `
<defs> <defs>
<filter id="invertMenuMore-${appId}"> <filter id="invertMenuMore-${appId}">
<feColorMatrix in="SourceGraphic" type="matrix" values="-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 0 0 0 1 0"></feColorMatrix> <feColorMatrix in="SourceGraphic" type="matrix" values="-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 0 0 0 1 0"></feColorMatrix>
</filter> </filter>
</defs>` </defs>`
svg.prepend(defs) svg.innerHTML = defs + svg.innerHTML
svg.find('image').attr('filter', `url(#invertMenuMore-${appId})`)
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 i = 0; i < 4000; i+= 100) {
setTimeout(updateTopMenu, i)
}
let resizeTimeout = null;
window.addEventListener('resize', () => {
if (resizeTimeout !== null) {
clearTimeout(resizeTimeout)
}
resizeTimeout = setTimeout(updateTopMenu, 100)
})
+137 -65
View File
@@ -1,59 +1,110 @@
(function() { <?php
var sideMenuContainer = jQuery('<div id="side-menu-container">')
var sideMenuOpener = jQuery('<button class="side-menu-opener"></button>')
var sideMenu = jQuery('<div id="side-menu">')
var body = jQuery('body')
var html = jQuery('html')
var isTouchDevice = window.matchMedia("(pointer: coarse)").matches
<?php if ($_['big-menu']): ?> $display = 'default';
sideMenu.attr('data-bigmenu', '1')
if ($_['always-displayed']) {
$display = 'always-displayed';
} elseif ($_['big-menu']) {
$display = 'big-menu';
} elseif ($_['side-with-categories']) {
$display = 'side-with-categories';
}
?>
(function() {
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 = <?php echo json_encode($_['target-blank-apps']) ?>
<?php if ($display === 'big-menu'): ?>
sideMenu.setAttribute('data-bigmenu', '1')
<?php elseif ($display === 'side-with-categories'): ?>
sideMenu.setAttribute('data-sidewithcategories', '1')
<?php endif; ?> <?php endif; ?>
var targetBlankApps = <?php echo json_encode($_['target-blank-apps']) ?>; querySelector('body').addEventListener('side-menu.apps', (e) => {
const apps = e.detail.apps;
body.on('side-menu.apps', function(e, apps) {
<?php if ($_['hide-when-no-apps']): ?> <?php if ($_['hide-when-no-apps']): ?>
sideMenu = jQuery('#side-menu') const sideMenu = querySelector('#side-menu')
if (apps.length === 0) { if (apps.length === 0) {
sideMenu.removeClass('open') sideMenu.classList.remove('open')
sideMenu.addClass('hide') sideMenu.classList.add('hide')
sideMenuOpener.addClass('hide') sideMenuOpener.classList.add('hide')
} else { } else {
sideMenu.removeClass('hide') sideMenu.classList.remove('hide')
sideMenuOpener.removeClass('hide') sideMenuOpener.classList.remove('hide')
} }
<?php if ($_['always-displayed'] && !$_['big-menu']): ?> <?php if ($display === 'always-displayed'): ?>
if (apps.length === 0) { if (apps.length === 0) {
html.removeClass('side-menu-always-displayed'); html.classList.remove('side-menu-always-displayed')
} else { } else {
html.addClass('side-menu-always-displayed'); html.classList.add('side-menu-always-displayed')
} }
<?php endif; ?> <?php endif; ?>
<?php else: ?> <?php else: ?>
<?php if ($_['always-displayed'] && !$_['big-menu']): ?> <?php if ($display === 'always-displayed'): ?>
if (apps.length === 0) { if (apps.length === 0) {
html.removeClass('side-menu-always-displayed'); html.classList.remove('side-menu-always-displayed')
} else { } else {
html.addClass('side-menu-always-displayed'); html.classList.add('side-menu-always-displayed')
} }
<?php endif; ?> <?php endif; ?>
<?php endif; ?> <?php endif; ?>
}) })
body.on('side-menu.ready', function() { body.addEventListener('side-menu.ready', () => {
sideMenu = jQuery('#side-menu') 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') sideMenuFocus = () => {
var sideMenuOpener = jQuery('#side-menu .side-menu-opener') let a = querySelector('.side-menu-app.active a', sideMenu)
sideMenuFocus = function() { if (!a) {
var a = sideMenu.find('.side-menu-app.active a') return
}
if (a.length === 0) { 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) { if (a.length > 0) {
@@ -61,84 +112,105 @@
} }
} }
<?php if ($_['opener-hover'] || ($_['always-displayed'] && !$_['big-menu'])): ?> <?php if ($_['opener-hover']): ?>
var sideMenuMouseLeave = function() { const sideMenuMouseLeave = () => {
sideMenu sideMenu.classList.remove('open')
.removeClass('open') sideMenu.removeEventListener('mouseleave', sideMenuMouseLeave)
.off('mouseleave', sideMenuMouseLeave)
} }
var sideMenuMouseEnter = function() { const sideMenuMouseEnter = () => {
sideMenu.on('mouseleave', sideMenuMouseLeave) sideMenu.addEventListener('mouseleave', sideMenuMouseLeave)
} }
var sideMenuOpenerMouseEnter = function() { const sideMenuOpenerMouseEnter = () => {
sideMenu sideMenu.classList.add('open')
.addClass('open') sideMenu.addEventListener('mouseenter', sideMenuMouseEnter)
.on('mouseenter', sideMenuMouseEnter)
sideMenuFocus() sideMenuFocus()
} }
if (!isTouchDevice) { if (!isTouchDevice) {
<?php if ($_['opener-hover']): ?> <?php if ($_['opener-hover']): ?>
headerMenuOpener.on('mouseenter', sideMenuOpenerMouseEnter) headerMenuOpener.addEventListener('mouseenter', sideMenuOpenerMouseEnter)
sideMenu.addClass('hide-opener') sideMenu.classList.add('hide-opener')
<?php endif ?> <?php endif ?>
sideMenu.on('mouseleave', sideMenuMouseLeave) sideMenu.addEventListener('mouseleave', sideMenuMouseLeave)
sideMenu.on('mouseenter', sideMenuOpenerMouseEnter) sideMenu.addEventListener('mouseenter', sideMenuOpenerMouseEnter)
} }
<?php endif; ?> <?php endif; ?>
headerMenuOpener.on('click', function() { headerMenuOpener.addEventListener('click', () => {
sideMenu.addClass('open') sideMenu.classList.add('open')
sideMenu.find('.side-menu-app.active a').focus()
const a = querySelector('.side-menu-app.active a', sideMenu)
if (a !== null) {
a.focus()
}
headerMenuOpener.blur()
}) })
<?php if ($_['always-displayed'] && !$_['big-menu']): ?> for (let opener of sideMenuOpener) {
sideMenuOpener.on('click', function() { opener.addEventListener('click', () => {
sideMenu.toggleClass('open') <?php if ($display === 'always-displayed'): ?>
sideMenu.classList.toggle('open')
<?php else: ?>
sideMenu.classList.remove('open')
<?php endif; ?>
}) })
<?php else: ?> }
sideMenuOpener.on('click', function() {
sideMenu.removeClass('open')
})
<?php endif; ?>
jQuery(document).keydown(function(e) { document.addEventListener('keydown', (e) => {
var key = e.key || e.keyCode var key = e.key || e.keyCode
if ((key === 'o' || key === 79) && e.ctrlKey === true) { if ((key === 'o' || key === 79) && e.ctrlKey === true) {
e.preventDefault() e.preventDefault()
sideMenu.toggleClass('open') sideMenu.classList.toggle('open')
sideMenuFocus() 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.append(sideMenuContainer) body.appendChild(sideMenuContainer)
sideMenuContainer.append(sideMenu) sideMenuContainer.appendChild(sideMenu)
<?php if ($_['loader-enabled'] === true): ?> <?php if ($_['loader-enabled'] === true): ?>
<?php require_once __DIR__.'/_loaderEnabled.js'; ?> <?php require_once __DIR__.'/_loaderEnabled.js'; ?>
<?php endif; ?> <?php endif; ?>
<?php if ($_['opener-position'] === 'before'): ?> <?php if ($_['opener-position'] === 'before'): ?>
sideMenuOpener.insertBefore('#nextcloud') nextcloud.parentNode.insertBefore(sideMenuOpener, nextcloud)
<?php else: ?> <?php else: ?>
sideMenuOpener.insertAfter('#nextcloud') nextcloud.parentNode.insertBefore(sideMenuOpener, nextcloud.nextSibling)
<?php endif; ?> <?php endif; ?>
<?php if (!empty($_['top-menu-apps'])): ?> <?php if (!empty($_['top-menu-apps']) || !empty($_['top-side-menu-apps'])): ?>
var topMenuApps = <?php echo json_encode($_['top-menu-apps']); ?>; const topMenuApps = <?php echo json_encode($_['top-menu-apps']), "\n"; ?>
const topSideMenuApps = <?php echo json_encode($_['top-side-menu-apps']); ?>
<?php require_once __DIR__.'/_topMenuApps.js'; ?> <?php require_once __DIR__.'/_topMenuApps.js'; ?>
<?php endif; ?> <?php endif; ?>
<?php if ($_['always-displayed'] && !$_['big-menu']): ?> <?php if ($display === 'always-displayed'): ?>
<?php require_once __DIR__.'/_alwaysDisplayed.js'; ?> <?php require_once __DIR__.'/_alwaysDisplayed.js'; ?>
<?php endif; ?> <?php endif; ?>
})(); })();
File diff suppressed because it is too large Load Diff
+143 -94
View File
@@ -16,6 +16,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
vendor_script('side_menu', 'html5sortable.min');
script('side_menu', 'admin'); script('side_menu', 'admin');
style('side_menu', 'admin'); style('side_menu', 'admin');
@@ -25,8 +26,6 @@ $choicesYesNo = [
]; ];
?> ?>
<div id="side-menu-section"> <div id="side-menu-section">
<?php if ($_['force']): ?> <?php if ($_['force']): ?>
<div class="section"> <div class="section">
@@ -44,71 +43,73 @@ $choicesYesNo = [
<?php p($l->t('Menu')); ?> <?php p($l->t('Menu')); ?>
</h2> </h2>
<div>
<label for="side-menu-enabled">
<?php p($l->t('Enable the custom menu')); ?>
</label>
</div>
<p> <p>
<em><?php echo $l->t('Use the shortcut <span class="keyboard-key">Ctrl</span>+<span class="keyboard-key">o</span> to open and to hide the side menu. Use <span class="keyboard-key">tab</span> to navigate.'); ?></em> <em><?php echo $l->t('Use the shortcut <span class="keyboard-key">Ctrl</span>+<span class="keyboard-key">o</span> to open and to hide the side menu. Use <span class="keyboard-key">tab</span> to navigate.'); ?></em>
</p> </p>
<div> <div class="side-menu-setting-table">
<select id="side-menu-enabled" name="enabled" class="side-menu-setting" data-personal> <div class="side-menu-setting-row">
<?php foreach ($choicesYesNo as $label => $value): ?> <div class="side-menu-setting-label">
<option value="<?php echo $value ?>" <?php if ($value === $_['enabled']): ?>selected<?php endif; ?>> <?php p($l->t('Enable the custom menu')); ?>
<?php echo $l->t($label); ?>
</option>
<?php endforeach; ?>
</select>
</div>
<div>
<label for="side-menu-target-blank">
<?php p($l->t('Open apps in new tab')); ?>
</label>
</div>
<div>
<?php $choices = [
'Use the global setting' => '1',
'Use my selection' => '2',
]; ?>
<select id="side-menu-loader-enabled" name="target-blank-mode" class="side-menu-setting" data-personal>
<?php foreach ($choices as $label => $value): ?>
<option value="<?php echo $value ?>" <?php if ($value === $_['target-blank-mode']): ?>selected<?php endif; ?>>
<?php echo $l->t($label); ?>
</option>
<?php endforeach; ?>
</select>
</div>
<p>
<a class="side-menu-toggler" data-target="#target-blank-apps" href="#_">
🖱️ <?php p($l->t('Show and hide the list of applications')); ?>
</a>
</p>
<div class="side-menu-setting" data-name="target-blank-apps" id="target-blank-apps" data-personal data-checkbox style="display: none">
<?php foreach ($_['apps'] as $app): ?>
<div>
<input
type="checkbox"
name="target-blank-apps[]"
value="<?php echo $app['id'] ?>"
id="target-blank-app-<?php echo $app['id'] ?>"
<?php if (in_array($app['id'], $_['target-blank-apps'])): ?>checked<?php endif; ?>
/>
<label for="target-blank-app-<?php echo $app['id'] ?>">
<?php echo p($l->t($app['name'])); ?>
</label>
</div> </div>
<?php endforeach; ?> <div class="side-menu-setting-form">
<select id="side-menu-enabled" name="enabled" class="side-menu-setting" data-personal>
<?php foreach ($choicesYesNo as $label => $value): ?>
<option value="<?php echo $value ?>" <?php if ($value === $_['enabled']): ?>selected<?php endif; ?>>
<?php echo $l->t($label); ?>
</option>
<?php endforeach; ?>
</select>
</div>
</div>
</div>
<div class="side-menu-setting-table">
<div class="side-menu-setting-row">
<div class="side-menu-setting-label">
<?php p($l->t('Open apps in new tab')); ?>
</div>
<div class="side-menu-setting-form">
<?php $choices = [
'Use the global setting' => '1',
'Use my selection' => '2',
]; ?>
<select id="side-menu-loader-enabled" name="target-blank-mode" class="side-menu-setting" data-personal>
<?php foreach ($choices as $label => $value): ?>
<option value="<?php echo $value ?>" <?php if ($value === $_['target-blank-mode']): ?>selected<?php endif; ?>>
<?php echo $l->t($label); ?>
</option>
<?php endforeach; ?>
</select>
<p>
<a class="side-menu-toggler" data-target="#target-blank-apps" href="#_">
🖱️ <?php p($l->t('Show and hide the list of applications')); ?>
</a>
</p>
<div class="side-menu-setting" data-name="target-blank-apps" id="target-blank-apps" data-personal data-checkbox style="display: none">
<ul class="side-menu-setting-list">
<?php foreach ($_['apps'] as $app): ?>
<li class="side-menu-setting-list-item">
<input
type="checkbox"
name="target-blank-apps[]"
value="<?php echo $app['id'] ?>"
id="target-blank-app-<?php echo $app['id'] ?>"
<?php if (in_array($app['id'], $_['target-blank-apps'])): ?>checked<?php endif; ?>
/>
<label for="target-blank-app-<?php echo $app['id'] ?>">
<?php echo p($l->t($app['name'])); ?>
</label>
</li>
<?php endforeach; ?>
</ul>
</div>
</div>
</div>
</div> </div>
</div> </div>
@@ -116,47 +117,95 @@ $choicesYesNo = [
<h2> <h2>
<?php p($l->t('Top menu')); ?> <?php p($l->t('Top menu')); ?>
</h2> </h2>
<div>
<label for="side-menu-top-menu-apps"> <div class="side-menu-setting-table">
<?php p($l->t('Apps that not must be moved in the side menu')); ?> <div class="side-menu-setting-row">
</label> <div class="side-menu-setting-label">
<?php p($l->t('Apps only visible in the top menu')); ?>
<p>
<em>
<?php p($l->t('If there is no selection then the global configuration is applied.')); ?>
</em>
</p>
</div>
<div class="side-menu-setting-form">
<p>
<a class="side-menu-toggler" data-target="#top-menu-apps" href="#_">
🖱️ <?php p($l->t('Show and hide the list of applications')); ?>
</a>
</p>
<div class="side-menu-setting" data-name="top-menu-apps" data-checkbox data-personal id="top-menu-apps" style="display: none">
<ul class="side-menu-setting-list">
<?php foreach ($_['apps'] as $app): ?>
<li class="side-menu-setting-list-item">
<input
type="checkbox"
name="top-menu-apps[]"
value="<?php echo $app['id'] ?>"
id="top-menu-app-<?php echo $app['id'] ?>"
<?php if (in_array($app['id'], $_['top-menu-apps'])): ?>checked<?php endif; ?>
/>
<label for="top-menu-app-<?php echo $app['id'] ?>">
<?php echo $app['name'] ?>
</label>
</li>
<?php endforeach; ?>
</ul>
</div>
</div>
</div>
</div> </div>
<p> <div class="side-menu-setting-table">
<em> <div class="side-menu-setting-row">
<?php p($l->t('If there is no selection then the global configuration is applied.')); ?> <div class="side-menu-setting-label">
</em> <?php p($l->t('Apps visible in the top and side menus')); ?>
</p> <p>
<em>
<p> <?php p($l->t('If there is no selection then the global configuration is applied.')); ?>
<a class="side-menu-toggler" data-target="#top-menu-apps" href="#_"> </em>
🖱️ <?php p($l->t('Show and hide the list of applications')); ?> </p>
</a>
</p>
<div class="side-menu-setting" data-name="top-menu-apps" data-checkbox data-personal id="top-menu-apps" style="display: none">
<?php foreach ($_['apps'] as $app): ?>
<div>
<input
type="checkbox"
name="top-menu-apps[]"
value="<?php echo $app['id'] ?>"
id="top-menu-app-<?php echo $app['id'] ?>"
<?php if (in_array($app['id'], $_['top-menu-apps'])): ?>checked<?php endif; ?>
/>
<label for="top-menu-app-<?php echo $app['id'] ?>">
<?php echo $app['name'] ?>
</label>
</div> </div>
<?php endforeach; ?> <div class="side-menu-setting-form">
<p>
<a class="side-menu-toggler" data-target="#top-side-menu-apps" href="#_">
🖱️ <?php p($l->t('Show and hide the list of applications')); ?>
</a>
</p>
<div class="side-menu-setting" data-name="top-side-menu-apps" data-checkbox data-personal id="top-side-menu-apps" style="display: none">
<ul class="side-menu-setting-list">
<?php foreach ($_['apps'] as $app): ?>
<li class="side-menu-setting-list-item">
<input
type="checkbox"
name="top-side-menu-apps[]"
value="<?php echo $app['id'] ?>"
id="top-side-menu-app-<?php echo $app['id'] ?>"
<?php if (in_array($app['id'], $_['top-side-menu-apps'])): ?>checked<?php endif; ?>
/>
<label for="top-side-menu-app-<?php echo $app['id'] ?>">
<?php echo $app['name'] ?>
</label>
</li>
<?php endforeach; ?>
</ul>
</div>
</div>
</div>
</div> </div>
</div> </div>
<?php endif ?> <?php endif ?>
<div class="section"> <div class="section">
<?php if (!$_['force']): ?> <?php if (!$_['force']): ?>
<button id="side-menu-save" class="btn btn-primary"><?php p($l->t('Save')); ?></button> <button id="side-menu-save" class="btn btn-info">
<?php p($l->t('Save')); ?>
<progress max="100" value="0" id="side-menu-save-progress"></progress>
</button>
<span id="side-menu-message" class="msg"></span> <span id="side-menu-message" class="msg"></span>
+2
View File
File diff suppressed because one or more lines are too long