Compare commits

..

4 Commits

Author SHA1 Message Date
Simon Vieille 711d169d22 Merge pull request 'add check if menu exists before adding event listeners (#210)' (#211) from feature/issue210 into v2.x
Reviewed-on: https://gitnet.fr/deblan/side_menu/pulls/211
2023-01-21 21:49:02 +01:00
Simon Vieille ddcd10f7b0 release v2.5.2 2023-01-21 21:45:41 +01:00
Simon Vieille ab8b745496 add check if menu exists before adding event listeners (#210) 2023-01-21 21:41:22 +01:00
Simon Vieille 401b351fbd include v2.x in CI 2022-10-11 22:49:02 +02:00
49 changed files with 1809 additions and 2269 deletions
-1
View File
@@ -4,4 +4,3 @@
/releases /releases
/package-lock.json /package-lock.json
!/l10n/.gitkeep !/l10n/.gitkeep
/yarn*.log
+1 -1
View File
@@ -1,5 +1,5 @@
{ {
"rules": { "rules": {
"indentation": 2 "indentation": 4
} }
} }
+6 -6
View File
@@ -1,21 +1,21 @@
pipeline: pipeline:
dependencies: dependencies:
image: gitnet.fr/deblan/devenv image: deblan/devenv
commands: commands:
- make dep - npm install
when: when:
event: [tag, push, pull_request] event: [tag, push, pull_request]
branch: [master, develop, feature/*] branch: [master, develop, v2.x, feature/*]
build: build:
image: gitnet.fr/deblan/devenv image: deblan/devenv
commands: commands:
- make build - make npm-build
when: when:
event: [push, pull_request] event: [push, pull_request]
package: package:
image: gitnet.fr/deblan/devenv image: deblan/devenv
volumes: volumes:
- /var/www/html/artifacts:/var/www/html/artifacts - /var/www/html/artifacts:/var/www/html/artifacts
secrets: [app_certificate] secrets: [app_certificate]
+2 -41
View File
@@ -1,47 +1,8 @@
## [Unreleased] ## [Unreleased]
## 3.3.2 ## 2.5.2
### Fixed ### Fixed
* fix #173: reduce the height of categories list * add check if menu exists before adding event listeners (#210)
## 3.3.1
### Fixed
* fix #162: top and side apps does work correctly
## 3.3.0
### Added
* add documentation in admin page
* add app sorter in user config side (#160)
### Fixed
* fix #164: open apps in new tab does not work
* fix #162 #159: top and side apps does work correctly
## 3.2.1
### Fixed
* fix #150: active app is not visible has active in menu (except in default menu)
* fix #151: opener position
## 3.2.0
### Added
* use custom app names using 'app.navigation.name' (#148)
* app sorting with all displays (#147)
## 3.1.0
### Added
* add global custom app sorting for the top menu
### Fixed
* fix admin list/modal look
## 3.0.1
### Fixed
* Remove the gap between the window's top and menu categories (large menu)
## 3.0.0
### Added
* Add compatibility with NC25 (#136/#135)
### Removed
* Nextcloud 20-24 are not supported anymore
* AppOrder is not supported anymore
## 2.5.1 ## 2.5.1
### Fixed ### Fixed
+3 -7
View File
@@ -1,15 +1,11 @@
build: dep npm-build:
npm run build npm run build
watch: dep npm-watch:
npm run watch npm run watch
dep:
npm i
npm link @nextcloud/vue || sudo npm link @nextcloud/vue
.ONESHELL: .ONESHELL:
release: build translations release: npm-build translations
if [ -z "$$VERSION" ]; then if [ -z "$$VERSION" ]; then
echo "VERSION required" echo "VERSION required"
exit 1 exit 1
+6 -18
View File
@@ -1,15 +1,12 @@
🤙 Nextcloud app / Custom menu 🎨 🤙 Nextcloud app / Custom menu 🎨
=============================== ===============================
[![Build Status](https://ci.gitnet.fr/api/badges/deblan/side_menu/status.svg)](https://ci.gitnet.fr/deblan/side_menu)
[![Translations](https://translate.codeberg.org/widgets/custom-menu/-/application/svg-badge.svg)](https://translate.codeberg.org/engage/custom-menu/)
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 add and sort custom categories, define apps that must be displayed in the top menu, etc. 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.
You can customize colors depending of the theme (Dark theme and Breeze Dark). 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)
@@ -18,6 +15,8 @@ You can customize colors depending of the theme (Dark theme and Breeze Dark).
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
------------ ------------
@@ -52,18 +51,14 @@ How to contribute?
You can report a bug or request a feature by opening an issue: https://gitnet.fr/deblan/side_menu/issues You can report a bug or request a feature by opening an issue: https://gitnet.fr/deblan/side_menu/issues
### You are a translator If you are a developer:
Translations are managed from [translate.codeberg.org](https://translate.codeberg.org/projects/custom-menu/application/).
### You are a developer
* fork the repository * fork the repository
* install an instance of Nextcloud * install an instance of Nextcloud
* go to `apps/` and clone your repository * go to `apps/` and clone your repository
* go to `apps/side_menu` and run `make dep` * go to `apps/side_menu` and run `npm install`
Build javascripts using `make build` (or `make 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.
@@ -71,10 +66,3 @@ Support
------- -------
You can join the official room on Matrix: [#custommenu:neutralnetwork.org](https://matrix.to/#/#custommenu:neutralnetwork.org). You can join the official room on Matrix: [#custommenu:neutralnetwork.org](https://matrix.to/#/#custommenu:neutralnetwork.org).
Notice
------
Because I believe in a free and decentralized Internet, [Gitnet](https://gitnet.fr) is **self-hosted at home**.
In case of downtime, you can download **Custom Menu** from [here](https://kim.deblan.fr/~side_menu/).
+3 -11
View File
@@ -11,7 +11,7 @@ This application is rather suitable for instances that activate a lot of applica
Use the shortcut `Ctrl`+`o` to open and to hide the side menu. Use `tab` to navigate. Use the shortcut `Ctrl`+`o` to open and to hide the side menu. Use `tab` to navigate.
You can customize colors depending of the theme (Dark theme and Breeze Dark). 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.
@@ -25,14 +25,8 @@ If you like this application and if you want to support the development:
* [Buy me a coffee](https://www.buymeacoffee.com/deblan) * [Buy me a coffee](https://www.buymeacoffee.com/deblan)
* [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)
Notice
------
Because I believe in a free and decentralized Internet, [Gitnet](https://gitnet.fr) is **self-hosted at home**.
In case of downtime, you can download **Custom Menu** from [here](https://kim.deblan.fr/~side_menu/).
]]></description> ]]></description>
<version>3.3.2</version> <version>2.5.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>
@@ -51,10 +45,8 @@ In case of downtime, you can download **Custom Menu** from [here](https://kim.de
<screenshot>https://gitnet.fr/deblan/side_menu/raw/branch/master/screenshots/nc18_menu_always_displayed.png</screenshot> <screenshot>https://gitnet.fr/deblan/side_menu/raw/branch/master/screenshots/nc18_menu_always_displayed.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/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>
<screenshot>https://gitnet.fr/deblan/side_menu/raw/branch/master/screenshots/nc25_big_menu.png</screenshot>
<screenshot>https://gitnet.fr/deblan/side_menu/raw/branch/master/screenshots/nc25_default_menu.png</screenshot>
<dependencies> <dependencies>
<nextcloud min-version="25" max-version="25"/> <nextcloud min-version="20" max-version="24"/>
<php min-version="7.4"/> <php min-version="7.4"/>
</dependencies> </dependencies>
<settings> <settings>
+8 -78
View File
@@ -18,8 +18,6 @@
#side-menu-section input[type="color"] { #side-menu-section input[type="color"] {
width: 100px; width: 100px;
margin: 10px 0 10px 0; margin: 10px 0 10px 0;
padding: 0;
border-radius: 0;
} }
#-dropside-menu-section input[type="checkbox"] { #-dropside-menu-section input[type="checkbox"] {
@@ -73,21 +71,14 @@
.side-menu-setting-list { .side-menu-setting-list {
margin: 10px 4px 4px 0px; margin: 10px 4px 4px 0px;
border: 2px solid var(--color-border-dark);
border-radius: 15px;
} }
.side-menu-setting-list-item { .side-menu-setting-list-item {
padding: 5px 10px; padding: 5px 10px;
border-bottom: 1px solid var(--color-border-dark); border: 1px solid var(--color-border-dark);
max-width: 300px; max-width: 300px;
margin: -1px 0 0 0; margin: -1px 0 0 0;
cursor: pointer; cursor: pointer;
line-height: 32px;
}
.side-menu-setting-list-item:last-child {
border-bottom: 0;
} }
.side-menu-setting-list-drop { .side-menu-setting-list-drop {
@@ -102,15 +93,15 @@
} }
.side-menu-setting-list-item input { .side-menu-setting-list-item input {
margin-top: 0; min-height: auto;
height: 21px !important; margin-top: -1px;
min-height: auto !important;
} }
#apps-categories-custom-list select { #apps-categories-custom-list select {
width: 100%; width: 100%;
} }
.side-menu-setting-table { .side-menu-setting-table {
display: table; display: table;
width: 100%; width: 100%;
@@ -118,22 +109,11 @@
.side-menu-setting-row { .side-menu-setting-row {
display: table; display: table;
margin-bottom: 10px;
}
.side-menu-setting-row code {
margin-left: 2px;
margin-bottom: 1px;
padding: 3px 10px;
border-radius: 5px;
display: inline-block;
right: 2px;
border: 1px solid var(--color-border-dark);
} }
.side-menu-setting-label { .side-menu-setting-label {
display: table-cell; display: table-cell;
width: 430px; width: 400px;
padding-right: 20px; padding-right: 20px;
} }
@@ -160,57 +140,7 @@
.btn-reset { .btn-reset {
display: inline-block; display: inline-block;
cursor: pointer; cursor: pointer;
position: relative; position: absolute;
top: -8px; margin-top: 17px;
left: 5px; margin-left: 5px;
transition-duration: 0.8s;
transition-property: transform;
transform: rotate(360deg);
}
.btn-reset--down {
top: 2px;
}
.btn-reset--progress {
transform: rotate(-359deg);
}
.badges {
margin-bottom: 14px;
margin-top: 4px;
}
.badge {
border-size: 1px;
padding: 2px 8px;
margin-right: 2px;
margin-bottom: 5px;
display: inline-block;
border-radius: 4px;
font-size: 13px;
}
.badge-1 {
background: #d4ce14;
border-color: #cad413;
color: #373a05;
}
.badge-2 {
background: #96d47f;
border-color: #7ed49b;
color: #333;
}
.badge-3 {
background: #d4540a;
border-color: #d4700c;
color: #fff;
}
.badge-4 {
background: #9d81d4;
border-color: #c681d4;
color: #fff;
} }
+17 -17
View File
@@ -21,7 +21,7 @@
left: 0; left: 0;
height: 100vh; height: 100vh;
width: 100%; width: 100%;
max-width: 290px; max-width: 250px;
background: linear-gradient(90deg, var(--side-menu-background-color, #333) 0%, var(--side-menu-background-color-to, #333) 100%); background: linear-gradient(90deg, var(--side-menu-background-color, #333) 0%, var(--side-menu-background-color-to, #333) 100%);
z-index: 3000; z-index: 3000;
color: var(--side-menu-text-color, #fff); color: var(--side-menu-text-color, #fff);
@@ -39,7 +39,6 @@
#header .side-menu-opener { #header .side-menu-opener {
margin-left: 0px; margin-left: 0px;
margin-top: -1px;
} }
.side-menu-settings { .side-menu-settings {
@@ -104,19 +103,27 @@
position: fixed; position: fixed;
top: 150px; top: 150px;
width: 100%; width: 100%;
max-width: 290px; max-width: 250px;
overflow: auto; overflow: auto;
} }
.side-menu-app-icon { .side-menu-app-icon {
width: 20px; width: 20px;
vertical-align: middle; vertical-align: top;
margin-top: -4px;
margin-right: 10px; margin-right: 10px;
filter: invert(var(--side-menu-icon-invert-filter, 0%)); filter: invert(var(--side-menu-icon-invert-filter, 0%));
opacity: var(--side-menu-icon-opacity, 1); opacity: var(--side-menu-icon-opacity, 1);
} }
.side-menu-app-icon svg {
vertical-align: middle;
margin-top: -3px;
}
.side-menu-app-icon .app-icon-notification {
display: none;
}
.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);
@@ -140,11 +147,11 @@
max-height: 100px; max-height: 100px;
} }
.enu-header { .side-menu-header {
height: 150px; height: 150px;
width: 100%; width: 100%;
z-index: 2300; z-index: 2300;
max-width: 290px; max-width: 250px;
position: fixed; position: fixed;
padding-top: 2px; padding-top: 2px;
top: 0; top: 0;
@@ -198,10 +205,10 @@
} }
.side-menu-categories { .side-menu-categories {
max-height: calc(100vh - 55px); max-height: calc(100vh - 50px);
overflow: auto; overflow: auto;
position: relative; position: relative;
top: 5px; top: 50px;
display: flex; display: flex;
flex-wrap: wrap; flex-wrap: wrap;
justify-content: center; justify-content: center;
@@ -233,6 +240,7 @@
margin-top: -2px; margin-top: -2px;
} }
.side-menu-always-displayed #header,
.side-menu-always-displayed body { .side-menu-always-displayed body {
width: calc(100% - 50px) !important; width: calc(100% - 50px) !important;
} }
@@ -304,14 +312,6 @@
overflow-x: visible; overflow-x: visible;
} }
.app-menu {
visibility: hidden;
}
.app-menu.show {
visibility: 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;
-8
View File
@@ -95,17 +95,14 @@ class JsController extends Controller
{ {
$topMenuApps = $this->config->getAppValueArray('top-menu-apps', '[]'); $topMenuApps = $this->config->getAppValueArray('top-menu-apps', '[]');
$topSideMenuApps = $this->config->getAppValueArray('top-side-menu-apps', '[]'); $topSideMenuApps = $this->config->getAppValueArray('top-side-menu-apps', '[]');
$topMenuAppsOrder = $this->config->getAppValueArray('top-menu-apps-order', '[]');
$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');
$addLogoLink = $this->config->getAppValueBool('add-logo-link', '1'); $addLogoLink = $this->config->getAppValueBool('add-logo-link', '1');
$appsOrder = $this->config->getAppValueArray('apps-order', '[]');
$avatar = null; $avatar = null;
$settings = null; $settings = null;
if ($this->user) { if ($this->user) {
$userAppsOrder = $this->config->getUserValueArray($this->user, 'apps-order', '[]');
$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', '[]'); $userTopSideMenuApps = $this->config->getUserValueArray($this->user, 'top-side-menu-apps', '[]');
@@ -117,10 +114,6 @@ class JsController extends Controller
$topSideMenuApps = $userTopSideMenuApps; $topSideMenuApps = $userTopSideMenuApps;
} }
if (!empty($userAppsOrder) && !$isForced) {
$appsOrder = $userAppsOrder;
}
$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', '[]');
@@ -168,7 +161,6 @@ class JsController extends Controller
'side-with-categories' => $this->config->getAppValueBool('side-with-categories', '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', '[]'),
'apps-order' => $appsOrder,
'avatar' => $avatar, 'avatar' => $avatar,
'top-menu-apps' => $topMenuApps, 'top-menu-apps' => $topMenuApps,
'top-side-menu-apps' => $topSideMenuApps, 'top-side-menu-apps' => $topSideMenuApps,
+9 -11
View File
@@ -93,13 +93,11 @@ class NavController extends Controller
]); ]);
} }
$apps = $this->appRepository->getOrderedApps($user); $apps = $this->appRepository->getVisibleApps();
$categoriesLabels = $this->categoryRepository->getOrderedCategories(); $categoriesLabels = $this->categoryRepository->getOrderedCategories();
$hiddenApps = $this->config->getAppValueArray('big-menu-hidden-apps', '[]'); $hiddenApps = $this->config->getAppValueArray('big-menu-hidden-apps', '[]');
$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', '[]');
$userTopSideMenuApps = $this->config->getUserValueArray($user, 'top-side-menu-apps', '[]');
$userTopMenuApps = $this->config->getUserValueArray($user, 'top-menu-apps', '[]'); $userTopMenuApps = $this->config->getUserValueArray($user, 'top-menu-apps', '[]');
$appsCategories = []; $appsCategories = [];
$categoriesAppsCount = []; $categoriesAppsCount = [];
@@ -108,16 +106,12 @@ class NavController extends Controller
$topMenuApps = $userTopMenuApps; $topMenuApps = $userTopMenuApps;
} }
if (!$isForced && !empty($userTopSideMenuApps)) { foreach ($apps as $app) {
$topSideMenuApps = $userTopSideMenuApps; if (in_array($app['id'], $topMenuApps)) {
continue;
} }
foreach ($apps as $app) { if (in_array($app['id'], $hiddenApps)) {
$inTopMenuApps = in_array($app['id'], $topMenuApps);
$inTopSideMenuApps = in_array($app['id'], $topSideMenuApps);
$inHiddenApps = in_array($app['id'], $hiddenApps);
if (($inTopMenuApps && !$inTopSideMenuApps) || $inHiddenApps) {
continue; continue;
} }
@@ -184,6 +178,10 @@ class NavController extends Controller
foreach ($items as $category => $value) { foreach ($items as $category => $value) {
if (empty($items[$category]['apps'])) { if (empty($items[$category]['apps'])) {
unset($items[$category]); unset($items[$category]);
} else {
uasort($items[$category]['apps'], function ($a, $b) {
return ($a['name'] < $b['name']) ? -1 : 1;
});
} }
} }
+1 -1
View File
@@ -97,7 +97,7 @@ class PersonalSettingController extends Controller
} }
} }
if (in_array($name, ['top-menu-apps', 'top-side-menu-apps', 'apps-order'])) { 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);
+7 -39
View File
@@ -2,7 +2,6 @@
namespace OCA\SideMenu\Service; namespace OCA\SideMenu\Service;
use OC\User\User;
use OCP\L10N\IFactory; use OCP\L10N\IFactory;
/** /**
@@ -37,7 +36,8 @@ class AppRepository
IFactory $l10nFactory, IFactory $l10nFactory,
ConfigProxy $config, ConfigProxy $config,
CategoryRepository $categoryRepository CategoryRepository $categoryRepository
) { )
{
$this->ocApp = $ocApp; $this->ocApp = $ocApp;
$this->l10nFactory = $l10nFactory; $this->l10nFactory = $l10nFactory;
$this->config = $config; $this->config = $config;
@@ -62,7 +62,7 @@ class AppRepository
foreach ([$app['id'], $app['id'].'_index'] as $id) { foreach ([$app['id'], $app['id'].'_index'] as $id) {
if (isset($navigation[$id])) { if (isset($navigation[$id])) {
$app['name'] = $this->getAppName($app); $app['name'] = $this->l10nFactory->get($id)->t($app['name']);
$app['href'] = $navigation[$id]['href']; $app['href'] = $navigation[$id]['href'];
$app['icon'] = $navigation[$id]['icon']; $app['icon'] = $navigation[$id]['icon'];
@@ -75,7 +75,7 @@ class AppRepository
if ('external_index' === substr($app['id'], 0, 14)) { if ('external_index' === substr($app['id'], 0, 14)) {
$visibleApps[$app['id']] = [ $visibleApps[$app['id']] = [
'id' => $app['id'], 'id' => $app['id'],
'name' => $this->getAppName($app), 'name' => $this->l10nFactory->get($app['id'])->t($app['name']),
'href' => $app['href'], 'href' => $app['href'],
'icon' => $app['icon'], 'icon' => $app['icon'],
'category' => [ 'category' => [
@@ -85,7 +85,7 @@ class AppRepository
} elseif ('files' === $app['id']) { } elseif ('files' === $app['id']) {
$visibleApps[$app['id']] = [ $visibleApps[$app['id']] = [
'id' => $app['id'], 'id' => $app['id'],
'name' => $this->getAppName($app), 'name' => $this->l10nFactory->get($app['id'])->t($app['name']),
'href' => $app['href'], 'href' => $app['href'],
'icon' => $app['icon'], 'icon' => $app['icon'],
'category' => [], 'category' => [],
@@ -99,42 +99,10 @@ class AppRepository
} }
} }
return $visibleApps; usort($visibleApps, function ($a, $b) {
}
public function getAppName($app)
{
return $this->config->getAppValue(
'app.navigation.name',
$this->l10nFactory->get($app['id'])->t($app['name']),
$app['id']
);
}
public function getOrderedApps(?User $user = null)
{
$apps = $this->getVisibleApps();
$orders = $this->config->getAppValueArray('apps-order', '[]');
if (null !== $user && !$this->config->getAppValueBool('force', '0')) {
$userOrders = $this->config->getUserValueArray($user, 'apps-order', '[]');
if (!empty($userOrders)) {
$orders = $userOrders;
}
}
usort($apps, function ($a, $b) use ($orders) {
$ak = array_keys($orders, $a['id'])[0] ?? null;
$bk = array_keys($orders, $b['id'])[0] ?? null;
if (null === $ak || null === $bk) {
return ($a['name'] < $b['name']) ? -1 : 1; return ($a['name'] < $b['name']) ? -1 : 1;
}
return $ak < $bk ? -1 : 1;
}); });
return $apps; return $visibleApps;
} }
} }
-2
View File
@@ -159,8 +159,6 @@ 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', '[]'),
'apps-order' => $this->config->getAppValueArray('apps-order', '[]'),
'ordered-apps' => $this->appRepository->getOrderedApps(),
'top-side-menu-apps' => $this->config->getAppValueArray('top-side-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(),
-2
View File
@@ -81,9 +81,7 @@ class Personal implements ISettings
'top-side-menu-apps' => $this->config->getUserValueArray($user, 'top-side-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-order' => $this->config->getUserValueArray($user, 'apps-order', '[]'),
'apps' => $this->appRepository->getVisibleApps(), 'apps' => $this->appRepository->getVisibleApps(),
'ordered-apps' => $this->appRepository->getOrderedApps($user),
]; ];
return new TemplateResponse(Application::APP_ID, 'settings/personal-form', $parameters, ''); return new TemplateResponse(Application::APP_ID, 'settings/personal-form', $parameters, '');
+11 -14
View File
@@ -2,15 +2,17 @@
"license": "agpl", "license": "agpl",
"private": true, "private": true,
"scripts": { "scripts": {
"build": "NODE_ENV=production ./node_modules/.bin/webpack-cli --progress --config webpack.js", "build": "NODE_ENV=production webpack --progress --config webpack.js",
"dev": "NODE_ENV=development ./node_modules/.bin/webpack-cli --progress --config webpack.js", "dev": "NODE_ENV=development webpack --progress --config webpack.js",
"watch": "NODE_ENV=development ./node_modules/.bin/webpack-cli --progress --watch --config webpack.js", "watch": "NODE_ENV=development webpack --progress --watch --config webpack.js",
"lint": "./node_modules/.bin/eslint --ext .js,.vue src", "lint": "eslint --ext .js,.vue src",
"lint:fix": "./node_modules/.bin/eslint --ext .js,.vue src --fix", "lint:fix": "eslint --ext .js,.vue src --fix",
"stylelint": "./node_modules/.bin/stylelint src", "stylelint": "stylelint src",
"stylelint:fix": "./node_modules/.bin/stylelint src --fix" "stylelint:fix": "stylelint src --fix"
}, },
"dependencies": { "dependencies": {
"@nextcloud/axios": "^1.8.0",
"@nextcloud/vue": "^1.5.0",
"axios": "^0.24.0", "axios": "^0.24.0",
"trim": "^1.0.1", "trim": "^1.0.1",
"vue": "^2.6.11" "vue": "^2.6.11"
@@ -25,12 +27,8 @@
"@babel/core": "^7.9.0", "@babel/core": "^7.9.0",
"@babel/plugin-syntax-dynamic-import": "^7.8.3", "@babel/plugin-syntax-dynamic-import": "^7.8.3",
"@babel/preset-env": "^7.9.0", "@babel/preset-env": "^7.9.0",
"@nextcloud/axios": "^1.8.0",
"@nextcloud/browserslist-config": "^1.0.0", "@nextcloud/browserslist-config": "^1.0.0",
"@nextcloud/eslint-config": "^8.1.2", "@nextcloud/eslint-config": "^8.1.2",
"@nextcloud/initial-state": "^2.0.0",
"@nextcloud/l10n": "^1.6.0",
"@nextcloud/vue": "^7.0.0",
"babel-eslint": "^10.1.0", "babel-eslint": "^10.1.0",
"babel-loader": "^8.1.0", "babel-loader": "^8.1.0",
"css-loader": "^3.4.2", "css-loader": "^3.4.2",
@@ -52,9 +50,8 @@
"stylelint-scss": "^4.0.0", "stylelint-scss": "^4.0.0",
"stylelint-webpack-plugin": "^3.3.0", "stylelint-webpack-plugin": "^3.3.0",
"url-loader": "^4.0.0", "url-loader": "^4.0.0",
"vue-loader": "^15", "vue-loader": "^15.9.1",
"vue-style-loader": "^4.1.3", "vue-template-compiler": "^2.6.11",
"vue-template-compiler": "^2.7.13",
"webpack": "^5.0.0", "webpack": "^5.0.0",
"webpack-cli": "^4.0.0", "webpack-cli": "^4.0.0",
"webpack-merge": "^4.2.2", "webpack-merge": "^4.2.2",
Binary file not shown.

Before

Width:  |  Height:  |  Size: 380 KiB

After

Width:  |  Height:  |  Size: 321 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 313 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 324 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 79 KiB

After

Width:  |  Height:  |  Size: 57 KiB

+38 -31
View File
@@ -22,72 +22,79 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
</li> </li>
</ul> </ul>
<NcActions> <Actions>
<NcActionButton @click="showAddForm" icon="icon-add"></NcActionButton> <ActionButton @click="showAddForm" icon="icon-add"></ActionButton>
</NcActions> </Actions>
<NcModal v-if="addForm" @close="hideAddForm"> <Modal v-if="addForm" @close="hideAddForm">
<div class="modal__content"> <div class="modal__content">
<div v-for="lang in langs"> <div v-for="lang in langs">
<span class="lang" v-text="lang"></span> <span class="lang" v-text="lang"></span>
<input type="text" v-model="newValue[lang]" required style="width: calc(100% - 100px)"> <input type="text" v-model="newValue[lang]" required>
</div> </div>
<NcActions> <Actions>
<NcActionButton @click="saveAdd" icon="icon-checkmark"></NcActionButton> <ActionButton @click="saveAdd" icon="icon-checkmark"></ActionButton>
</NcActions> </Actions>
</div> </div>
</NcModal> </Modal>
<NcModal v-if="editForm" @close="hideEditForm"> <Modal v-if="editForm" @close="hideEditForm">
<div class="modal__content"> <div class="modal__content">
<div v-for="lang in langs"> <div v-for="lang in langs">
<span class="lang" v-text="lang"></span> <span class="lang" v-text="lang"></span>
<input type="text" v-model="editValue[lang]" required style="width: calc(100% - 100px)"> <input type="text" v-model="editValue[lang]" required>
</div> </div>
<div class="pull-right"> <div class="pull-right">
<NcActions> <Actions>
<NcActionButton @click="removeEdit" icon="icon-delete"></NcActionButton> <ActionButton @click="removeEdit" icon="icon-delete"></ActionButton>
</NcActions> </Actions>
</div> </div>
<NcActions> <Actions>
<NcActionButton @click="saveEdit" icon="icon-checkmark"></NcActionButton> <ActionButton @click="saveEdit" icon="icon-checkmark"></ActionButton>
</NcActions> </Actions>
</div> </div>
</NcModal> </Modal>
</div> </div>
</template> </template>
<style> <style scoped>
.modal__content { .modal__content {
width: 200px;
padding: 10px; padding: 10px;
} }
.modal__content .lang { .modal__content .lang {
width: 60px; width: 60px;
display: inline-block; display: inline-block;
padding: 4px; padding: 4px;
box-sizing: border-box; box-sizing: border-box;
} }
.pull-right { .modal__content input[type=text] {
width: calc(100% - 85px);
display: inline-block;
margin: 0;
}
.pull-right {
float: right; float: right;
} }
</style> </style>
<script> <script>
import NcModal from '@nextcloud/vue/dist/Components/NcModal' import Modal from '@nextcloud/vue/dist/Components/Modal'
import NcActions from '@nextcloud/vue/dist/Components/NcActions' import Actions from '@nextcloud/vue/dist/Components/Actions'
import NcActionButton from '@nextcloud/vue/dist/Components/NcActionButton' import ActionButton from '@nextcloud/vue/dist/Components/ActionButton'
export default { export default {
name: 'AdminCategoriesCustom', name: 'AdminCategoriesCustom',
components: { components: {
NcModal, Modal,
NcActions, Actions,
NcActionButton, ActionButton,
}, },
data() { data() {
return { return {
-312
View File
@@ -1,312 +0,0 @@
<!--
- @copyright Copyright (c) 2022 Julius Härtl <jus@bitgrid.net>
-
- @author Julius Härtl <jus@bitgrid.net>
-
- @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>
<nav class="app-menu show">
<ul class="app-menu-main" v-if="apps !== null">
<li v-for="app in mainAppList()"
:key="app.id"
:data-app-id="app.id"
class="app-menu-entry"
:class="{ 'app-menu-entry__active': app.active }"
:style="makeStyle(app)"
>
<a :href="app.href"
:class="{ 'has-unread': app.unread > 0 }"
:aria-label="appLabel(app)"
:target="targetBlankApps.indexOf(app.id) !== -1 ? '_blank' : undefined"
:aria-current="app.active ? 'page' : false">
<img :src="app.icon" alt="">
<div class="app-menu-entry--label">
{{ app.name }}
<span v-if="app.unread > 0" class="hidden-visually unread-counter">{{ app.unread }}</span>
</div>
</a>
</li>
</ul>
<NcActions class="app-menu-more" :aria-label="t('core', 'More apps')" v-if="apps !== null">
<NcActionLink v-for="app in popoverAppList()"
:key="app.id"
:aria-label="appLabel(app)"
:aria-current="app.active ? 'page' : false"
:href="app.href"
class="app-menu-popover-entry">
<template #icon>
<div class="app-icon" :class="{ 'has-unread': app.unread > 0 }">
<img :src="app.icon" alt="">
</div>
</template>
{{ app.name }}
<span v-if="app.unread > 0" class="hidden-visually unread-counter">{{ app.unread }}</span>
</NcActionLink>
</NcActions>
</nav>
</template>
<script>
import { loadState } from '@nextcloud/initial-state'
import { NcActions, NcActionLink } from '@nextcloud/vue'
export default {
name: 'AppMenu',
components: {
NcActions, NcActionLink,
},
data() {
return {
apps: null,
appLimit: 0,
observer: null,
targetBlankApps: [],
}
},
mounted() {
const ncApps = loadState('core', 'apps', {})
this.apps = {}
let orders = {}
window.menuAppsOrder.forEach((app, order) => {
orders[app] = order + 1
})
Array.from(window.topMenuApps).forEach((id) => {
if (ncApps.hasOwnProperty(id)) {
this.apps[id] = ncApps[id]
this.apps[id].order = orders[id] || null
}
})
this.targetBlankApps = window.targetBlankApps
this.observer = new ResizeObserver(this.resize)
this.observer.observe(this.$el)
this.resize()
},
beforeDestroy() {
this.observer.disconnect()
},
methods: {
appLabel() {
return (app) => app.name
+ (app.active ? ' (' + t('core', 'Currently open') + ')' : '')
+ (app.unread > 0 ? ' (' + n('core', '{count} notification', '{count} notifications', app.unread, { count: app.unread }) + ')' : '')
},
appList() {
return Object.values(this.apps)
},
mainAppList() {
return this.appList().slice(0, this.appLimit)
},
popoverAppList() {
return this.appList().slice(this.appLimit)
},
setNavigationCounter(id, counter) {
this.$set(this.apps[id], 'unread', counter)
},
resize() {
const availableWidth = this.$el.offsetWidth
let appCount = Math.floor(availableWidth / 50) - 1
const popoverAppCount = this.appList.length - appCount
if (popoverAppCount === 1) {
appCount--
}
if (appCount < 1) {
appCount = 0
}
this.appLimit = appCount
},
makeStyle(app) {
if (app.order !== null) {
return `order: ${app.order}`
}
}
},
}
</script>
<style lang="scss" scoped>
$header-icon-size: 20px;
.app-menu {
width: 100%;
display: flex;
flex-shrink: 1;
flex-wrap: wrap;
}
.app-menu-main {
display: flex;
flex-wrap: nowrap;
.app-menu-entry {
width: 50px;
height: 50px;
position: relative;
display: flex;
opacity: .7;
&.app-menu-entry__active {
opacity: 1;
&::before {
content: " ";
position: absolute;
pointer-events: none;
border-bottom-color: var(--color-main-background);
transform: translateX(-50%);
width: 12px;
height: 5px;
border-radius: 3px;
background-color: var(--color-primary-text);
left: 50%;
bottom: 6px;
display: block;
transition: all 0.1s ease-in-out;
opacity: 1;
}
.app-menu-entry--label {
font-weight: bold;
}
}
a {
width: calc(100% - 4px);
height: calc(100% - 4px);
margin: 2px;
color: var(--color-primary-text);
position: relative;
}
img {
transition: margin 0.1s ease-in-out;
width: $header-icon-size;
height: $header-icon-size;
padding: calc((100% - $header-icon-size) / 2);
filter: var(--primary-invert-if-bright);
}
.app-menu-entry--label {
opacity: 0;
position: absolute;
font-size: 12px;
color: var(--color-primary-text);
text-align: center;
bottom: -5px;
left: 50%;
display: block;
min-width: 100%;
transform: translateX(-50%);
transition: all 0.1s ease-in-out;
width: 100%;
text-overflow: ellipsis;
overflow: hidden;
}
&:hover,
&:focus-within {
opacity: 1;
.app-menu-entry--label {
opacity: 1;
font-weight: bold;
font-size: 14px;
bottom: 0;
width: auto;
overflow: visible;
}
}
}
// Show labels
&:hover,
&:focus-within,
.app-menu-entry:hover,
.app-menu-entry:focus {
opacity: 1;
img {
margin-top: -6px;
}
.app-menu-entry--label {
opacity: 1;
bottom: 0;
}
&::before, .app-menu-entry::before {
opacity: 0;
}
}
}
::v-deep .app-menu-more .button-vue--vue-tertiary {
color: var(--color-primary-text);
opacity: .7;
margin: 3px;
&:hover {
opacity: 1;
background-color: transparent !important;
}
&:focus-visible {
opacity: 1;
background-color: transparent !important;
border-radius: var(--border-radius);
outline: none;
box-shadow: 0 0 0 2px var(--color-primary-text);
}
}
.app-menu-popover-entry {
.app-icon {
position: relative;
height: 44px;
&.has-unread::after {
background-color: var(--color-main-text);
}
img {
filter: var(--background-invert-if-bright);
width: $header-icon-size;
height: $header-icon-size;
padding: calc((50px - $header-icon-size) / 2);
}
}
}
.has-unread::after {
content: "";
width: 8px;
height: 8px;
background-color: var(--color-primary-text);
border-radius: 50%;
position: absolute;
display: block;
top: 10px;
right: 10px;
}
.unread-counter {
display: none;
}
</style>
-20
View File
@@ -1,20 +0,0 @@
const createElement = require('./lib/createElement')
const PageLoader = () => {
const pageLoader = createElement('div', {id: 'side-menu-loader'})
const pageLoaderBar = createElement('div', {id: 'side-menu-loader-bar'})
pageLoader.appendChild(pageLoaderBar)
document.querySelector('body').appendChild(pageLoader)
let pageLoaderValue = 0
window.addEventListener('beforeunload', () => {
setInterval(() => {
pageLoaderBar.style.width = pageLoaderValue.toString() + '%'
pageLoaderValue = Math.min(pageLoaderValue + .2, 100)
}, 25)
})
}
module.exports = PageLoader
+1 -1
View File
@@ -20,7 +20,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
{{ label }} {{ label }}
<span class="avatardiv avatardiv-shown"> <span class="avatardiv avatardiv-shown">
<img v-bind:src="avatar"> <img v-bind:src="avatar" v-bind:alt="name" v-bind:title="name">
</span> </span>
</a> </a>
</div> </div>
+12 -33
View File
@@ -16,56 +16,35 @@
*/ */
import Vue from 'vue' import Vue from 'vue'
import AppMenu from './AppMenu.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' import SideMenuWithCategories from './SideMenuWithCategories.vue'
import PageLoader from './PageLoader'
import SMcreateElement from './lib/createElement'
Vue.prototype.OC = OC Vue.prototype.OC = OC
Vue.prototype.t = OC.L10N.translate
window.SMcreateElement = SMcreateElement
window.PageLoader = PageLoader
const mountSideMenuComponent = () => { const mountSideMenuComponent = () => {
const container = document.querySelector('#side-menu') const sideMenuContainer = document.querySelector('#side-menu')
if (!container) { if (sideMenuContainer) {
return window.setTimeout(mountSideMenuComponent, 50) let component
}
const component = (() => { if (sideMenuContainer.getAttribute('data-bigmenu')) {
if (container.getAttribute('data-bigmenu')) { component = SideMenuBig
return SideMenuBig } else if(sideMenuContainer.getAttribute('data-sidewithcategories')) {
} else if(container.getAttribute('data-sidewithcategories')) { component = SideMenuWithCategories
return SideMenuWithCategories
} else { } else {
return SideMenu component = SideMenu
} }
})()
const View = Vue.extend(component) const View = Vue.extend(component)
const App = new View({}) const sideMenu = new View({})
App.$mount('#side-menu') sideMenu.$mount('#side-menu')
document.querySelector('body').dispatchEvent(new CustomEvent('side-menu.ready')) document.querySelector('body').dispatchEvent(new CustomEvent('side-menu.ready'))
} } else {
window.setTimeout(mountSideMenuComponent, 50)
const mountAppMenu = () => {
const container = document.querySelector('#header .app-menu')
if (!container) {
return window.setTimeout(mountAppMenu, 50)
} }
const View = Vue.extend(AppMenu)
const App = new View({})
App.$mount('#header .app-menu')
} }
mountSideMenuComponent() mountSideMenuComponent()
mountAppMenu()
+65 -36
View File
@@ -40,9 +40,8 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
<ul class="side-menu-apps-list"> <ul class="side-menu-apps-list">
<SideMenuApp <SideMenuApp
v-for="(app, key) in apps" v-for="app in apps"
v-bind:classes="{'side-menu-app': true, 'active': app.active}" v-bind:classes="{'side-menu-app': true, 'active': app.active}"
v-bind:key="key"
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"
@@ -59,7 +58,6 @@ import OpenerButton from './OpenerButton'
import SettingsButton from './SettingsButton' import SettingsButton from './SettingsButton'
import SideMenuApp from './SideMenuApp' import SideMenuApp from './SideMenuApp'
import Logo from './Logo' import Logo from './Logo'
import { loadState } from '@nextcloud/initial-state'
export default { export default {
name: 'SideMenu', name: 'SideMenu',
@@ -82,58 +80,89 @@ export default {
}, },
methods: { methods: {
retrieveApps() { retrieveApps() {
const ncApps = loadState('core', 'apps', {}) this.apps = []
let orders = {} const links = document.querySelectorAll('#appmenu a')
let finalApps = [] const menu = document.querySelector('#appmenu')
let menuIsHidden = true
window.menuAppsOrder.forEach((app, order) => { if (menu) {
orders[app] = order + 1 menuIsHidden = window.getComputedStyle(menu, null).getPropertyValue('display') === 'none'
}
for (let element of links) {
let href = element.getAttribute('href')
let parent = element.parentNode
if (!parent) {
continue
}
let dataId = parent.getAttribute('data-id')
dataId = dataId !== null ? dataId : ''
if (!parent.classList.contains('app-top-side-menu') && !parent.classList.contains('app-hidden') && !menuIsHidden) {
continue
}
if (href !== '#') {
let svg = element.querySelector('svg').outerHTML
svg = svg
.replace(/(height|width)="20"/, '')
.replace('id="invertMenuMain', 'id="invertSideMenu')
.replace('url(#invertMenuMain', 'url(#invertSideMenu')
if (this.forceLightIcon) {
svg = svg.replace(/filter="url[^"]+"/, '')
}
this.apps.push({
id: dataId,
href: href,
name: trim(element.querySelector('span').innerHTML),
icon: svg,
active: element.classList.contains('active')
}) })
}
for (let id in ncApps) {
if (window.topMenuApps.includes(id) && !window.topSideMenuApps.includes(id)) {
continue;
} }
let app = ncApps[id] (function(apps) {
app.order = orders[id] || null window.setTimeout(function() {
finalApps.push(app)
}
finalApps.sort((a, b) => {
if (a.order === null || b.order === null) {
return a.name < b.name ? -1 : 1;
}
return a.order < b.order ? -1 : 1;
})
this.apps = finalApps
document.querySelector('body').dispatchEvent(new CustomEvent('side-menu.apps', { document.querySelector('body').dispatchEvent(new CustomEvent('side-menu.apps', {
detail: {apps: this.apps}, detail: {apps: apps},
})) }))
}, 1000)
})(this.apps)
}, },
retrieveConfig() { retrieveConfig() {
let that = this
axios axios
.get(OC.generateUrl('/apps/side_menu/js/config')) .get(OC.generateUrl('/apps/side_menu/js/config'))
.then((response) => { .then(function(response) {
const config = response.data const config = response.data
this.targetBlankApps = config['target-blank-apps'] that.targetBlankApps = config['target-blank-apps']
this.forceLightIcon = config['force-light-icon'] that.forceLightIcon = config['force-light-icon']
this.avatar = config['avatar'] that.avatar = config['avatar']
this.logo = config['logo'] that.logo = config['logo']
this.logoLink = config['logo-link'] that.logoLink = config['logo-link']
this.settings = config['settings'] that.settings = config['settings']
}) })
}, },
}, },
mounted() { mounted() {
this.retrieveConfig() this.retrieveConfig()
this.retrieveApps() this.retrieveApps()
const menu = document.querySelector('#appmenu')
if (menu) {
const config = {attributes: true, childList: true, subtree: true}
const observer = new MutationObserver(this.retrieveApps)
observer.observe(menu, config)
}
} }
} }
</script> </script>
+1 -1
View File
@@ -17,7 +17,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
<template> <template>
<li v-bind:class="classes"> <li v-bind:class="classes">
<a v-bind:href="href" :target="target" v-bind:title="label"> <a v-bind:href="href" :target="target" v-bind:title="label">
<img class="side-menu-app-icon" v-bind:src="icon" v-bind:alt="label" /> <span class="side-menu-app-icon" v-html="icon"></span>
<span class="side-menu-app-text" v-text="label"></span> <span class="side-menu-app-text" v-text="label"></span>
</a> </a>
</li> </li>
+15 -16
View File
@@ -33,13 +33,12 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
<div class="side-menu-categories"> <div class="side-menu-categories">
<Loader v-if="!items.length" /> <Loader v-if="!items.length" />
<div class="side-menu-category" v-for="(category, key) in items" v-bind:key="key"> <div class="side-menu-category" v-for="category in items">
<h2 class="side-menu-category-title" v-if="category.name != ''" v-text="category.name"></h2> <h2 class="side-menu-category-title" v-if="category.name != ''" v-text="category.name"></h2>
<ul class="side-menu-apps-list"> <ul class="side-menu-apps-list">
<SideMenuBigApp <SideMenuBigApp
v-for="(app, appId) in category.apps" v-for="(app, appId) in category.apps"
v-bind:key="appId"
v-bind:classes="{'side-menu-app': true, 'active': activeApp === appId}" v-bind:classes="{'side-menu-app': true, 'active': activeApp === appId}"
v-bind:icon="app.icon" v-bind:icon="app.icon"
v-bind:label="app.name" v-bind:label="app.name"
@@ -60,7 +59,6 @@ import CloserButton from './CloserButton'
import SettingsButton from './SettingsButton' import SettingsButton from './SettingsButton'
import Loader from './Loader' import Loader from './Loader'
import SideMenuBigApp from './SideMenuBigApp' import SideMenuBigApp from './SideMenuBigApp'
import { loadState } from '@nextcloud/initial-state'
export default { export default {
name: 'SideMenuBig', name: 'SideMenuBig',
@@ -82,13 +80,17 @@ export default {
}, },
methods: { methods: {
retrieveApps() { retrieveApps() {
this.apps = []
let that = this
axios axios
.get(OC.generateUrl('/apps/side_menu/nav/items')) .get(OC.generateUrl('/apps/side_menu/nav/items'))
.then((response) => { .then(function(response) {
this.items = response.data.items that.items = response.data.items
let apps = [] let apps = []
for (let category of this.items) { for (let category of that.items) {
for (let a in category.apps) { for (let a in category.apps) {
apps.push(category.apps[a]) apps.push(category.apps[a])
} }
@@ -101,23 +103,20 @@ export default {
}, },
retrieveActiveApp() { retrieveActiveApp() {
const ncApps = loadState('core', 'apps', {}) let activeAppLink = document.querySelector('#appmenu a.active')
this.activeApp = activeAppLink ? activeAppLink.parentNode.getAttribute('data-id') : null
for (let id in ncApps) {
if (ncApps[id].active) {
this.activeApp = id
}
}
}, },
retrieveConfig() { retrieveConfig() {
let that = this
axios axios
.get(OC.generateUrl('/apps/side_menu/js/config')) .get(OC.generateUrl('/apps/side_menu/js/config'))
.then((response) => { .then(function(response) {
const config = response.data const config = response.data
this.targetBlankApps = config['target-blank-apps'] that.targetBlankApps = config['target-blank-apps']
this.settings = config['settings'] that.settings = config['settings']
}) })
}, },
}, },
+17 -16
View File
@@ -31,13 +31,12 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
<div class="side-menu-categories"> <div class="side-menu-categories">
<Loader v-if="!items.length" /> <Loader v-if="!items.length" />
<div class="side-menu-category" v-for="(category, key) in items" v-bind:key="key"> <div class="side-menu-category" v-for="category in items">
<h2 class="side-menu-category-title" v-if="category.name != ''" v-text="category.name"></h2> <h2 class="side-menu-category-title" v-if="category.name != ''" v-text="category.name"></h2>
<ul class="side-menu-apps-list"> <ul class="side-menu-apps-list">
<SideMenuBigApp <SideMenuBigApp
v-for="(app, appId) in category.apps" v-for="(app, appId) in category.apps"
v-bind:key="appId"
v-bind:classes="{'side-menu-app': true, 'active': activeApp === appId}" v-bind:classes="{'side-menu-app': true, 'active': activeApp === appId}"
v-bind:icon="app.icon" v-bind:icon="app.icon"
v-bind:label="app.name" v-bind:label="app.name"
@@ -54,16 +53,17 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
<script> <script>
import axios from 'axios' import axios from 'axios'
import OpenerButton from './OpenerButton' import OpenerButton from './OpenerButton'
import CloserButton from './CloserButton'
import SettingsButton from './SettingsButton' import SettingsButton from './SettingsButton'
import Loader from './Loader' import Loader from './Loader'
import SideMenuBigApp from './SideMenuBigApp' import SideMenuBigApp from './SideMenuBigApp'
import { loadState } from '@nextcloud/initial-state'
export default { export default {
name: 'SideMenuWithCategories', name: 'SideMenuWithCategories',
components: { components: {
SettingsButton, SettingsButton,
OpenerButton, OpenerButton,
CloserButton,
Loader, Loader,
SideMenuBigApp, SideMenuBigApp,
}, },
@@ -78,13 +78,17 @@ export default {
}, },
methods: { methods: {
retrieveApps() { retrieveApps() {
this.apps = []
let that = this
axios axios
.get(OC.generateUrl('/apps/side_menu/nav/items')) .get(OC.generateUrl('/apps/side_menu/nav/items'))
.then((response) => { .then(function(response) {
this.items = response.data.items that.items = response.data.items
let apps = [] let apps = []
for (let category of this.items) { for (let category of that.items) {
for (let a in category.apps) { for (let a in category.apps) {
apps.push(category.apps[a]) apps.push(category.apps[a])
} }
@@ -97,23 +101,20 @@ export default {
}, },
retrieveActiveApp() { retrieveActiveApp() {
const ncApps = loadState('core', 'apps', {}) let activeAppLink = document.querySelector('#appmenu a.active')
this.activeApp = activeAppLink ? activeAppLink.parentNode.getAttribute('data-id') : null
for (let id in ncApps) {
if (ncApps[id].active) {
this.activeApp = id
}
}
}, },
retrieveConfig() { retrieveConfig() {
let that = this
axios axios
.get(OC.generateUrl('/apps/side_menu/js/config')) .get(OC.generateUrl('/apps/side_menu/js/config'))
.then((response) => { .then(function(response) {
const config = response.data const config = response.data
this.targetBlankApps = config['target-blank-apps'] that.targetBlankApps = config['target-blank-apps']
this.settings = config['settings'] that.settings = config['settings']
}) })
}, },
}, },
+1 -23
View File
@@ -159,15 +159,9 @@ document.addEventListener('DOMContentLoaded', () => {
const target = event.target const target = event.target
const values = JSON.parse(target.getAttribute('data-reset')) const values = JSON.parse(target.getAttribute('data-reset'))
target.classList.toggle('btn-reset--progress', true)
for (let i in values) { for (let i in values) {
document.querySelector(`#${i}`).value = values[i] document.querySelector(`#${i}`).value = values[i]
} }
window.setTimeout(() => {
target.classList.toggle('btn-reset--progress', false)
}, 800)
}) })
} }
@@ -255,6 +249,7 @@ document.addEventListener('DOMContentLoaded', () => {
let value = [] let value = []
for (let item of document.querySelectorAll('#categories-list .side-menu-setting-list-item')) { 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')) value.push(item.getAttribute('data-id'))
} }
@@ -262,21 +257,4 @@ document.addEventListener('DOMContentLoaded', () => {
}) })
} catch (e) { } catch (e) {
} }
sortable('#apps-order-list .side-menu-setting-list', {
placeholderClass: 'side-menu-setting-list-drop'
})
try {
sortable('#apps-order-list .side-menu-setting-list')[0].addEventListener('sortstop', (e) => {
let value = []
for (let item of document.querySelectorAll('#apps-order-list .side-menu-setting-list-item')) {
value.push(item.getAttribute('data-id'))
}
document.querySelector('input[name="apps-order"]').value = JSON.stringify(value)
})
} catch (e) {
}
}) })
+16 -34
View File
@@ -3,21 +3,13 @@
"No": "Ne" "No": "Ne"
"Yes": "Ano" "Yes": "Ano"
"Menu": "Nabídka" "Menu": "Nabídka"
? 'Use the shortcut <span class="keyboard-key">Ctrl</span>+<span class="keyboard-key">o</span> '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> („O“ jako otevřít). Pro pohyb po použijte klávesu <span class="keyboard-key">Tab</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> („O“ jako otevřít). Pro pohyb po použijte klávesu
<span class="keyboard-key">Tab</span>.'
"Top menu": "Horní nabídka" "Top menu": "Horní nabídka"
"Apps that not must be moved in the side menu": "Aplikace, které nepřesouvat do postranní\ "Apps that not must be moved in the side menu": "Aplikace, které nepřesouvat do postranní nabídky"
\ 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í."
"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í" "Experimental": "Experimentální"
"Save": "Uložit" "Save": "Uložit"
"You like this app and you want to support me?": "Líbí se vám tato aplikace a chcete\ "You like this app and you want to support me?": "Líbí se vám tato aplikace a chcete podpořit její vývoj?"
\ podpořit její vývoj?"
"Buy me a coffee ☕": "Kupte mi kafe ☕" "Buy me a coffee ☕": "Kupte mi kafe ☕"
"Hidden": "Skryté" "Hidden": "Skryté"
"Small": "Malé" "Small": "Malé"
@@ -44,21 +36,19 @@
"After the logo": "Za logem" "After the logo": "Za logem"
"Position": "Pozice" "Position": "Pozice"
"Show only the opener (hidden logo)": "Zobrazovat pouze otevírací tlačítko (logo skryto)" "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\ "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)."
\ postranní nabídku a její otevírací tlačítko pokud nejsou dostupné žádné aplikace\
\ (např. na veřejných stránkách)."
"Panel": "Panel" "Panel": "Panel"
"Open the menu when the mouse is hover the opener (automatically disabled on touch screens)": "Otevřít\ "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)."
\ 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" "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" "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" "Icons and texts": "Ikony a texty"
"Loader enabled": "Načítání zapnuto" "Loader enabled": "Načítání zapnuto"
"Tips": "Tipy" "Tips": "Tipy"
"Always displayed": "Vždy zobrazeno" "Always displayed": "Vždy zobrazeno"
"This is the automatic behavior when the menu is always displayed.": "Toto je automatické\ "The logo will be hidden when the menu is always displayed.": "Pokud je nabídka zobrazena trvale, logo bude skryto."
\ chování, kdy je nabídka vždy zobrazena." "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." "Not compatible with touch screens.": "Nekompatibilní s dotykovými obrazovkami."
"Big menu": "Velká nabídka" "Big menu": "Velká nabídka"
"Live preview": "Živý náhled" "Live preview": "Živý náhled"
@@ -72,13 +62,10 @@
"Export the configuration": "Exportovat nastavení" "Export the configuration": "Exportovat nastavení"
"Purge the cache": "Vyprázdnit mezipaměť" "Purge the cache": "Vyprázdnit mezipaměť"
"Show the link to settings": "Zobrazit odkaz na nastavení" "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 pro uživatele\ "The menu is enabled by default for users": "Nabídka je ve výchozím stavu pro uživatele zapnutá"
\ zapnutá"
"Except when the configuration is forced.": "S výjimkou, kdy je nastavení vynuceno." "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\ "Apps that should not be displayed in the menu": "Aplikace, které by neměly být v nabídce zobrazeny"
\ 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>."
"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" "The logo is a link to the default app": "Logo je odkaz na výchozí aplikaci"
"Others": "Ostatní" "Others": "Ostatní"
"Categories": "Kategorie" "Categories": "Kategorie"
@@ -87,12 +74,13 @@
"Name": "Název" "Name": "Název"
"Customed": "Přizpůsobeno" "Customed": "Přizpůsobeno"
"Show and hide the list of categories": "Zobrazit/skrýt seznam kategorií" "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\ "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."
\ parametry jsou použity v případě, že je zapnutý (Breeze) tmavý motiv vzhledu."
"Dark mode colors": "Barvy tmavého režimu" "Dark mode colors": "Barvy tmavého režimu"
"With categories": "S kategoriemi" "With categories": "S kategoriemi"
"Custom categories": "Vlastní kategorie" "Custom categories": "Vlastní kategorie"
"Customize application categories": "Přizpůsobte kategorie aplikací" "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" "Reset to default": "Vrátit zpět na výchozí hodnoty"
"Hidden icon": "Skrytá ikona" "Hidden icon": "Skrytá ikona"
"Small icon": "Malá ikona" "Small icon": "Malá ikona"
@@ -102,9 +90,3 @@
"Small text": "Malý text" "Small text": "Malý text"
"Normal text": "Normální text" "Normal text": "Normální text"
"Big text": "Velký text" "Big text": "Velký text"
"Applications": "Applications"
"Applications kept in the top menu": "Applications kept in the top menu"
"Applications kept in the top menu but also shown in side menu": "Applications kept\
\ in the top menu but also shown in side menu"
"These applications must be selected in the previous option.": "These applications\
\ must be selected in the previous option."
+18 -39
View File
@@ -3,21 +3,13 @@
"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> '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.'
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": "Obere Navigationsleiste" "Top menu": "Obere Navigationsleiste"
"Apps that not must be moved in the side menu": "Anwendungen, die nicht ins Seitenmenü\ "Apps that not must be moved in the side menu": "Anwendungen, die nicht ins Seitenmenü verschoben werden sollen"
\ 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 Anwendung und möchtest\ "You like this app and you want to support me?": "Du magst diese Anwendung und möchtest mich unterstützen?"
\ mich unterstützen?"
"Buy me a coffee ☕": "Gib mir einen Kaffee aus ☕" "Buy me a coffee ☕": "Gib mir einen Kaffee aus ☕"
"Hidden": "Ausblenden" "Hidden": "Ausblenden"
"Small": "Klein" "Small": "Klein"
@@ -44,21 +36,19 @@
"After the logo": "Nach dem Logo" "After the logo": "Nach dem Logo"
"Position": "Position" "Position": "Position"
"Show only the opener (hidden logo)": "Nur das Menü-Symbol anzeigen (Logo wird ausgeblendet)" "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\ "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)."
\ 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\ "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)."
\ 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"
"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 dem <code>großen Menü</code> kompatibel."
"Icons and texts": "Symbole und Texte" "Icons and texts": "Symbole und Texte"
"Loader enabled": "Ladestandanzeige aktiviert" "Loader enabled": "Ladestandanzeige aktiviert"
"Tips": "Tipps" "Tips": "Tipps"
"Always displayed": "Immer anzeigen" "Always displayed": "Immer anzeigen"
"This is the automatic behavior when the menu is always displayed.": "Dies ist das\ "The logo will be hidden when the menu is always displayed.": "Das Logo wird ausgeblendet, wenn das Menü immer angezeigt wird."
\ 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"
@@ -67,20 +57,15 @@
"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": "Avatar anstelle des Logos anzeigen" "Use the avatar instead of the logo": "Avatar anstelle des Logos anzeigen"
"You do not have permission to change the settings.": "Du hast keine Berechtigung,\ "You do not have permission to change the settings.": "Du hast keine Berechtigung, die Einstellungen dieser Anwendung zu ändern."
\ die Einstellungen dieser Anwendung zu ändern."
"Force this configuration to users": "Konfiguration für alle Benutzer erzwingen" "Force this configuration to users": "Konfiguration für alle Benutzer erzwingen"
"Export the configuration": "Konfiguration exportieren" "Export the configuration": "Konfiguration exportieren"
"Purge the cache": "Cache leeren" "Purge the cache": "Cache leeren"
"Show the link to settings": "Link zu den Einstellungen anzeigen" "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 alle Benutzer\ "The menu is enabled by default for users": "Das Menü ist standardmäßig für alle Benutzer aktiviert"
\ aktiviert" "Except when the configuration is forced.": "Gilt nicht, wenn die Konfiguration erzwungen wird."
"Except when the configuration is forced.": "Gilt nicht, wenn die Konfiguration erzwungen\ "Apps that should not be displayed in the menu": "Anwendungen, die nicht im Menü angezeigt werden sollen"
\ wird." "This feature is only compatible with the <code>big menu</code> display.": "Kompatibel mit dem <code>großen Menü</code>."
"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 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" "Others": "Andere"
"Categories": "Kategorien" "Categories": "Kategorien"
@@ -89,13 +74,13 @@
"Name": "Name" "Name": "Name"
"Customed": "Benutzerdefiniert" "Customed": "Benutzerdefiniert"
"Show and hide the list of categories": "Liste der Kategorien ein- und ausblenden" "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\ "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."
\ Optionen werden auf <code>Dark Theme</code> oder <code>Breeze Dark Theme</code>\
\ angewendet."
"Dark mode colors": "Farben für den dunklen Modus" "Dark mode colors": "Farben für den dunklen Modus"
"With categories": "Mit Kategorien" "With categories": "Mit Kategorien"
"Custom categories": "Benutzerdefinierte Kategorien" "Custom categories": "Benutzerdefinierte Kategorien"
"Customize application categories": "Anwendungskategorien anpassen" "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" "Reset to default": "Auf Standard zurücksetzen"
"Hidden icon": "Verstecktes Symbol" "Hidden icon": "Verstecktes Symbol"
"Small icon": "Kleines Symbol" "Small icon": "Kleines Symbol"
@@ -105,9 +90,3 @@
"Small text": "Kleiner Text" "Small text": "Kleiner Text"
"Normal text": "Normaler Text" "Normal text": "Normaler Text"
"Big text": "Großer Text" "Big text": "Großer Text"
"Applications": "Applications"
"Applications kept in the top menu": "Applications kept in the top menu"
"Applications kept in the top menu but also shown in side menu": "Applications kept\
\ in the top menu but also shown in side menu"
"These applications must be selected in the previous option.": "These applications\
\ must be selected in the previous option."
+7 -7
View File
@@ -3,11 +3,10 @@
"No": "Non" "No": "Non"
"Yes": "Oui" "Yes": "Oui"
"Menu": "Menu" "Menu": "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.' '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.': 'Utiliser le raccourcis clavier <span class="keyboard-key">Ctrl</span>+<span class="keyboard-key">o</span> pour ouvrir et fermer le menu latéral. Utiliser <span class="keyboard-key">tab</span> pour naviguer.'
: 'Utiliser le raccourcis clavier <span class="keyboard-key">Ctrl</span>+<span class="keyboard-key">o</span> pour ouvrir et fermer le menu latéral. Utiliser <span class="keyboard-key">tab</span> pour naviguer.'
"Top menu": "Menu supérieur" "Top menu": "Menu supérieur"
"Apps that not must be moved in the side menu": "Les applications qui ne doivent pas être affichées dans le menu latéral" "Apps that not must be moved in the side menu": "Les applications qui ne doivent pas être affichées dans le menu latéral"
"If there is no selection then the global configuration is applied.": "Si il n'y a aucune sélection alors la configuration globale sera appliquée." "If there is no selection then the global configuration is applied.": "Si il n'y a aucune sélection alors la configuration globale sera appliquée"
"Experimental": "Expérimental" "Experimental": "Expérimental"
"Save": "Sauvegarder" "Save": "Sauvegarder"
"You like this app and you want to support me?": "Vous aimer cette application et vous souhaitez m'aider ?" "You like this app and you want to support me?": "Vous aimer cette application et vous souhaitez m'aider ?"
@@ -49,11 +48,14 @@
"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"
"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."
"Icons and texts": "Icônes et textes" "Icons and texts": "Icônes et textes"
"Loader enabled": "Activation de l'indicateur de chargement" "Loader enabled": "Activation de l'indicateur de chargement"
"Tips": "Astuces" "Tips": "Astuces"
"Always displayed": "Toujours affiché" "Always displayed": "Toujours affiché"
"The logo will be hidden when the menu is always displayed.": "Le logo sera masque si le menu est toujours affiché."
"This is the automatic behavior when the menu is always displayed.": "C'est le comportement automatique lorsque le menu est toujours affiché." "This is the automatic behavior when the menu is always displayed.": "C'est le comportement automatique lorsque le menu est toujours affiché."
"Not compatible with touch screens.": "Incompatible avec les écrans tactiles." "Not compatible with touch screens.": "Incompatible avec les écrans tactiles."
"Big menu": "Menu large" "Big menu": "Menu large"
@@ -85,8 +87,6 @@
"With categories": "Avec les catégories" "With categories": "Avec les catégories"
"Custom categories": "Catégories personnalisées" "Custom categories": "Catégories personnalisées"
"Customize application categories": "Personnaliser les catégories des applications" "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" "Reset to default": "Restaurer les valeurs par défaut"
"Applications": "Applications"
"Applications kept in the top menu": "Applications conservées dans le menu supérieur"
"Applications kept in the top menu but also shown in side menu": "Applications conservées dans le menu supérieur mais également affichées dans le menu latéral"
"These applications must be selected in the previous option.": "Ces applications doivent également être sélectionnées dans l'option précédente."
-94
View File
@@ -1,94 +0,0 @@
"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.'
: ''
"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": ""
"Hidden icon": ""
"Small icon": ""
"Normal icon": ""
"Big icon": ""
"Hidden text": ""
"Small text": ""
"Normal text": ""
"Big text": ""
"Colors": ""
"Background color": ""
"Background color of current app": ""
"Text color": ""
"Loader": ""
"Icon": ""
"Same color": ""
"Opposite color": ""
"Transparent": ""
"Opaque": ""
"Opener": ""
"Default": ""
"Default (dark)": ""
"Hamburger": ""
"Hamburger (dark)": ""
"Hamburger 2": ""
"Hamburger 2 (dark)": ""
"Before the logo": ""
"After the logo": ""
"Position": ""
"Show only the opener (hidden logo)": ""
"Do not display the side menu and the opener if there is no application (eg: public pages).": ""
"Panel": ""
"Open the menu when the mouse is hover the opener (automatically disabled on touch screens)": ""
"Display the big menu": ""
"Display the logo": ""
"Icons and texts": ""
"Loader enabled": ""
"Tips": ""
"Always displayed": ""
"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": ""
"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.": ""
"The logo is a link to the default app": ""
"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": ""
"Reset to default": ""
"Applications": ""
"Applications kept in the top menu": ""
"Applications kept in the top menu but also shown in side menu": ""
"These applications must be selected in the previous option.": ""
+9 -16
View File
@@ -3,11 +3,7 @@
"No": "取消" "No": "取消"
"Yes": "确定" "Yes": "确定"
"Menu": "菜单" "Menu": "菜单"
? "Use the shortcut <span class=\"keyboard-key\">Ctrl</span>+<span class=\"keyboard-key\"\ "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> 来导航。"
>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": "顶部菜单" "Top menu": "顶部菜单"
"Apps that not must be moved in the side menu": "禁止在侧边栏菜单移动的应用" "Apps that not must be moved in the side menu": "禁止在侧边栏菜单移动的应用"
"If there is no selection then the global configuration is applied.": "如不选择,将应用全局设定。" "If there is no selection then the global configuration is applied.": "如不选择,将应用全局设定。"
@@ -40,17 +36,18 @@
"After the logo": "在logo后" "After the logo": "在logo后"
"Position": "位置" "Position": "位置"
"Show only the opener (hidden logo)": "只显示容器 (隐藏logo)" "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如果没有应用,不显示侧边栏菜单和容器\ "Do not display the side menu and the opener if there is no application (eg: public pages).": "N如果没有应用,不显示侧边栏菜单和容器 (例如 : 公共页面)。"
\ (例如 : 公共页面)。"
"Panel": "面板" "Panel": "面板"
"Open the menu when the mouse is hover the opener (automatically disabled on touch screens)": "鼠标悬停时打开菜单\ "Open the menu when the mouse is hover the opener (automatically disabled on touch screens)": "鼠标悬停时打开菜单 (触摸屏时将自动禁用)"
\ (触摸屏时将自动禁用)"
"Display the big menu": "显示大型菜单" "Display the big menu": "显示大型菜单"
"This menu is not compatible with AppOrder.": "型菜单与应用顺序不兼容"
"Display the logo": "显示logo" "Display the logo": "显示logo"
"This feature is not compatible with the <code>big menu<\/code> display.": "此功能与显示<code>大型菜单<\/code>不兼容。"
"Icons and texts": "图标与文字" "Icons and texts": "图标与文字"
"Loader enabled": "菜单指示器已激活" "Loader enabled": "菜单指示器已激活"
"Tips": "技巧" "Tips": "技巧"
"Always displayed": "一直显示" "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.": "一直显示菜单时的自动动作。" "This is the automatic behavior when the menu is always displayed.": "一直显示菜单时的自动动作。"
"Not compatible with touch screens.": "与触屏不兼容。" "Not compatible with touch screens.": "与触屏不兼容。"
"Big menu": "大型菜单" "Big menu": "大型菜单"
@@ -68,7 +65,7 @@
"The menu is enabled by default for users": "用户的默认菜单已激活" "The menu is enabled by default for users": "用户的默认菜单已激活"
"Except when the configuration is forced.": "除非设置被强制使用。" "Except when the configuration is forced.": "除非设置被强制使用。"
"Apps that should not be displayed in the menu": "禁止在菜单中显示的应用" "Apps that should not be displayed in the menu": "禁止在菜单中显示的应用"
"This feature is only compatible with the <code>big menu</code> display.": "此功能只和<code>大型菜单</code>兼容。" "This feature is only compatible with the <code>big menu<\/code> display.": "此功能只和<code>大型菜单<\/code>兼容。"
"The logo is a link to the default app": "logo链接到默认应用" "The logo is a link to the default app": "logo链接到默认应用"
"Others": "其他" "Others": "其他"
"Categories": "类别" "Categories": "类别"
@@ -82,6 +79,8 @@
"With categories": "有类别" "With categories": "有类别"
"Custom categories": "自定义类别" "Custom categories": "自定义类别"
"Customize application categories": "自定义应用程序类别" "Customize application categories": "自定义应用程序类别"
"Apps only visible in the top menu": "应用程序仅在顶部菜单中可见"
"Apps visible in the top and side menus": "顶部和侧边菜单中可见的应用程序"
"Reset to default": "重置为默认设置" "Reset to default": "重置为默认设置"
"Hidden icon": "隐藏图标" "Hidden icon": "隐藏图标"
"Small icon": "小图标" "Small icon": "小图标"
@@ -91,9 +90,3 @@
"Small text": "小文本" "Small text": "小文本"
"Normal text": "普通文本" "Normal text": "普通文本"
"Big text": "大文本" "Big text": "大文本"
"Applications": "Applications"
"Applications kept in the top menu": "Applications kept in the top menu"
"Applications kept in the top menu but also shown in side menu": "Applications kept\
\ in the top menu but also shown in side menu"
"These applications must be selected in the previous option.": "These applications\
\ must be selected in the previous option."
-11
View File
@@ -1,11 +0,0 @@
module.exports = (tagName, attributes) => {
const element = document.createElement(tagName)
if (typeof attributes === 'object') {
for (let i in attributes) {
element.setAttribute(i, attributes[i])
}
}
return element
}
-12
View File
@@ -114,15 +114,3 @@
font-size: 16px; font-size: 16px;
} }
<?php endif; ?> <?php endif; ?>
<?php if ($_['always-displayed']): ?>
#content {
left: 53px;
width: calc(100% - (var(--body-container-margin) * 2) - 62px);
}
#content-vue {
width: calc(100% - (var(--body-container-margin) * 2) - 60px);
margin-left: 11px;
}
<?php endif; ?>
+86
View File
@@ -0,0 +1,86 @@
const alwaysDisplayed = function() {
const elements = querySelectorAll('*')
const fixedElements = []
for (let element of elements) {
if (typeof element !== 'object') {
continue
}
const position = window.getComputedStyle(element, null).getPropertyValue('position')
if (position !== 'fixed') {
continue
}
const id = element.getAttribute('id')
if (id === 'header' || id === 'side-menu' || id === 'side-menu-loader') {
continue
}
if (element.classList.contains('oc-dialog')) {
continue
}
let elementIsInSideMenu = false
let parent = element.parentNode
while (parent && !elementIsInSideMenu) {
try {
if (parent.getAttribute('id') === 'side-menu') {
elementIsInSideMenu = true
}
} catch (e) {
}
parent = parent.parentNode
}
if (elementIsInSideMenu) {
continue
}
fixedElements.push(element)
}
for (let i in fixedElements) {
const element = fixedElements[i]
const computedStyle = window.getComputedStyle(element, null)
const left = computedStyle.getPropertyValue('left')
const right = computedStyle.getPropertyValue('right')
if (right !== '0px') {
const intValue = parseInt(left.replace('px', '')) + 50
element.style.setProperty('transform', 'translateX(' + intValue.toString() + 'px)')
}
}
}
const content = querySelector('#content')
if (content && content.classList.contains('app-settings')) {
let loaded = false
const config = {
attributes: false,
childList: true,
subtree: true
}
const observer = new MutationObserver(() => {
if (loaded) {
return
}
const element = content.querySelector('#app-category-your-apps') || content.querySelector('#app-navigation ul')
if (element) {
loaded = true
alwaysDisplayed()
}
})
observer.observe(content, config)
} else {
window.setTimeout(alwaysDisplayed, 200)
}
+14
View File
@@ -0,0 +1,14 @@
let pageLoader = createElement('div', {id: 'side-menu-loader'})
let pageLoaderBar = createElement('div', {id: 'side-menu-loader-bar'})
pageLoader.appendChild(pageLoaderBar)
querySelector('body').appendChild(pageLoader)
let pageLoaderValue = 0
window.addEventListener('beforeunload', () => {
setInterval(() => {
pageLoaderBar.style.width = pageLoaderValue.toString() + '%'
pageLoaderValue = Math.min(pageLoaderValue + .2, 100)
}, 25)
})
+216
View File
@@ -0,0 +1,216 @@
let menuCache = null
const breakpointMobileWidth = 1024
const usePercentualAppMenuLimit = 0.8
const minAppsDesktop = 8
const handleMenuClick = (e, icon) => {
let element = e.target
while (element.tagName !== 'LI') {
element = element.parentNode
}
const a = querySelector('a', element)
if (a.getAttribute('target') !== '_blank' && e.which === 1 && !e.ctrlKey && !e.metaKey) {
for (let tag of ['svg', 'div']) {
let el = querySelector(tag, element)
if (el) {
el.remove()
}
}
const loader = createElement('div', {'class': icon})
a.insertBefore(loader, querySelector('span', a))
}
}
const updateTopMenu = function() {
const isMobile = window.innerWidth < breakpointMobileWidth
const menu = querySelector('#appmenu')
const moreApps = querySelector('#more-apps')
const navigation = querySelector('#navigation')
const navigationApps = querySelector('#apps ul')
let apps = querySelectorAll('li', menu)
let lastShownApp = null
let appShown = []
if ((menu.innerHTML + menu.nextSibling.innerHTML) === menuCache) {
return
}
let navigationAppsHtml = ''
for (let app of apps) {
const dataId = app.getAttribute('data-id')
if (dataId === null) {
continue
}
if (topMenuApps.indexOf(dataId) === -1 && topSideMenuApps.indexOf(dataId) === -1) {
app.classList.add('hidden')
app.classList.add('app-hidden')
} else {
app.classList.remove('hidden')
app.classList.add('app-external-site')
if (topSideMenuApps.indexOf(dataId) !== -1) {
app.classList.add('app-top-side-menu')
}
appShown.push(app)
navigationAppsHtml = navigationAppsHtml + app.outerHTML
}
if (targetBlankApps.indexOf(dataId) !== -1) {
querySelector('a', app).setAttribute('target', '_blank')
}
}
navigationApps.innerHTML = navigationAppsHtml
const rightHeaderWidth = querySelector('.header-right').offsetWidth
const headerWidth = querySelector('header').offsetWidth
let availableWidth = headerWidth
availableWidth -= nextcloud.offsetWidth
availableWidth -= querySelector('#header .side-menu-opener').offsetWidth
availableWidth -= rightHeaderWidth > 230 ? rightHeaderWidth : 230
availableWidth *= isMobile ? usePercentualAppMenuLimit : 1
let appCount = Math.floor(availableWidth / querySelector('#appmenu li:not(.hidden)').offsetWidth)
if (isMobile && appCount > minAppsDesktop) {
appCount = minAppsDesktop
} else if (!isMobile && appCount < minAppsDesktop) {
appCount = minAppsDesktop
}
menu.style.opacity = 1
if (appShown.length - 1 - appCount >= 1) {
appCount--
}
for (let item of querySelectorAll('a', moreApps)) {
item.classList.remove('active')
}
let k = 0
let notInHeader = 0
for (let app of appShown) {
const name = app.getAttribute('data-id')
const li = querySelector('#apps li[data-id=' + name + '].app-external-site')
if (k < appCount && appCount > 0) {
app.classList.remove('hidden')
li.classList.add('in-header')
lastShownApp = app
} else {
app.classList.add('hidden')
li.classList.remove('in-header')
notInHeader++
const a = querySelector('a', app)
if (appCount > 0 && a.classList.contains('active')) {
lastShownApp.classList.add('hidden')
app.classList.remove('hidden')
notInHeader++
li.classList.add('in-header')
}
}
k++
}
// Hack for:
// - https://github.com/nextcloud/server/blob/master/core/src/components/MainMenu.js#L97-L119
// - https://github.com/nextcloud/server/blob/master/core/src/components/MainMenu.js#L97-L119
jQuery(menu).undelegate('li:not(#more-apps) > a', 'click')
jQuery(navigation).undelegate('a', 'click')
const confs = [
{
items: querySelectorAll('#navigation li'),
icon: 'icon-loading-small'
},
{
items: querySelectorAll('li:not(#more-apps)', menu),
icon: OCA.Theming && OCA.Theming.inverted ? 'icon-loading-small' : 'icon-loading-small-dark'
},
]
for (let conf of confs) {
for (let item of conf.items) {
item.addEventListener('click', (e) => {
handleMenuClick(e, conf.icon)
})
}
}
for (let app of querySelectorAll('#apps li.app-external-site')) {
const appId = app.getAttribute('data-id')
if (app.classList.contains('in-header')) {
for (let defs of querySelectorAll('svg defs', app)) {
defs.remove()
}
} else {
const svg = querySelector('svg', app)
if (querySelectorAll('svg defs', app).length > 0) {
continue
}
const defs = `
<defs>
<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>
</filter>
</defs>`
svg.innerHTML = defs + svg.innerHTML
for (let image of querySelectorAll('image', svg)) {
image.setAttribute('filter', `url(#invertMenuMore-${appId})`)
}
svg.innerHTML = svg.innerHTML.replace(/fecolormatrix/g, 'feColorMatrix')
}
}
if (notInHeader === 0) {
moreApps.style.display = 'none'
navigation.style.display = 'none'
} else {
moreApps.style.display = 'flex'
}
menuCache = menu.innerHTML + menu.nextSibling.innerHTML
}
for (let i = 0; i < 4000; i+= 100) {
setTimeout(updateTopMenu, i)
}
let resizeTimeout = null;
window.addEventListener('resize', () => {
if (resizeTimeout !== null) {
clearTimeout(resizeTimeout)
}
resizeTimeout = setTimeout(updateTopMenu, 100)
})
+61 -20
View File
@@ -13,20 +13,44 @@ if ($_['always-displayed']) {
?> ?>
(function() { (function() {
const sideMenuContainer = SMcreateElement('div', {id: 'side-menu-container'}) const querySelector = function(selector, element) {
const sideMenuOpener = SMcreateElement('button', {'class': 'side-menu-opener'}) if (element) {
const sideMenu = SMcreateElement('div', {id: 'side-menu'}) return element.querySelector(selector)
}
const body = document.querySelector('body') return document.querySelector(selector)
const html = document.querySelector('html') }
const nextcloud = document.querySelector('#nextcloud')
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 isTouchDevice = window.matchMedia("(pointer: coarse)").matches
const targetBlankApps = <?php echo json_encode($_['target-blank-apps']) ?>
window.targetBlankApps = <?php echo json_encode($_['target-blank-apps']), "\n" ?>
window.topMenuApps = <?php echo json_encode($_['top-menu-apps']), "\n"; ?>
window.topSideMenuApps = <?php echo json_encode($_['top-side-menu-apps']), "\n"; ?>
window.menuAppsOrder = <?php echo json_encode($_['apps-order']), "\n"; ?>
<?php if ($display === 'big-menu'): ?> <?php if ($display === 'big-menu'): ?>
sideMenu.setAttribute('data-bigmenu', '1') sideMenu.setAttribute('data-bigmenu', '1')
@@ -34,11 +58,11 @@ if ($_['always-displayed']) {
sideMenu.setAttribute('data-sidewithcategories', '1') sideMenu.setAttribute('data-sidewithcategories', '1')
<?php endif; ?> <?php endif; ?>
document.querySelector('body').addEventListener('side-menu.apps', (e) => { querySelector('body').addEventListener('side-menu.apps', (e) => {
const apps = e.detail.apps; const apps = e.detail.apps;
<?php if ($_['hide-when-no-apps']): ?> <?php if ($_['hide-when-no-apps']): ?>
const sideMenu = document.querySelector('#side-menu') const sideMenu = querySelector('#side-menu')
if (apps.length === 0) { if (apps.length === 0) {
sideMenu.classList.remove('open') sideMenu.classList.remove('open')
@@ -68,19 +92,23 @@ if ($_['always-displayed']) {
}) })
body.addEventListener('side-menu.ready', () => { body.addEventListener('side-menu.ready', () => {
const sideMenu = document.querySelector('#side-menu') const sideMenu = querySelector('#side-menu')
const headerMenuOpener = document.querySelector('#header .side-menu-opener') const headerMenuOpener = querySelector('#header .side-menu-opener')
const sideMenuOpener = document.querySelectorAll('#side-menu .side-menu-opener') const sideMenuOpener = querySelectorAll('#side-menu .side-menu-opener')
if (!headerMenuOpener) {
return
}
sideMenuFocus = () => { sideMenuFocus = () => {
let a = document.querySelector('.side-menu-app.active a', sideMenu) let a = querySelector('.side-menu-app.active a', sideMenu)
if (!a) { if (!a) {
return return
} }
if (a.length === 0) { if (a.length === 0) {
a = sideMenu.querySelector('.side-menu-app:first-child a') a = querySelector('.side-menu-app:first-child a', sideMenu)
} }
if (a.length > 0) { if (a.length > 0) {
@@ -120,7 +148,7 @@ if ($_['always-displayed']) {
headerMenuOpener.addEventListener('click', () => { headerMenuOpener.addEventListener('click', () => {
sideMenu.classList.add('open') sideMenu.classList.add('open')
const a = sideMenu.querySelector('.side-menu-app.active a') const a = querySelector('.side-menu-app.active a', sideMenu)
if (a !== null) { if (a !== null) {
a.focus() a.focus()
@@ -170,12 +198,25 @@ if ($_['always-displayed']) {
sideMenuContainer.appendChild(sideMenu) sideMenuContainer.appendChild(sideMenu)
<?php if ($_['loader-enabled'] === true): ?> <?php if ($_['loader-enabled'] === true): ?>
PageLoader() <?php require_once __DIR__.'/_loaderEnabled.js'; ?>
<?php endif; ?> <?php endif; ?>
if (nextcloud) {
<?php if ($_['opener-position'] === 'before'): ?> <?php if ($_['opener-position'] === 'before'): ?>
nextcloud.parentNode.insertBefore(sideMenuOpener, nextcloud) nextcloud.parentNode.insertBefore(sideMenuOpener, nextcloud)
<?php else: ?> <?php else: ?>
nextcloud.parentNode.insertBefore(sideMenuOpener, nextcloud.nextSibling) nextcloud.parentNode.insertBefore(sideMenuOpener, nextcloud.nextSibling)
<?php endif; ?> <?php endif; ?>
}
<?php if (!empty($_['top-menu-apps']) || !empty($_['top-side-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 endif; ?>
<?php if ($display === 'always-displayed'): ?>
<?php require_once __DIR__.'/_alwaysDisplayed.js'; ?>
<?php endif; ?>
})(); })();
+112 -207
View File
@@ -41,104 +41,8 @@ $choicesSizes = [
$labelShowHideApps = 'Show and hide the list of applications'; $labelShowHideApps = 'Show and hide the list of applications';
$labelReset = 'Reset to default'; $labelReset = 'Reset to default';
?> ?>
<div id="side-menu-section"> <div id="side-menu-section">
<div class="section">
<h2>
<?php p($l->t('Panel')); ?>
</h2>
<?php
$displays = [
'default' => !$_['always-displayed'] && !$_['big-menu'] && !$_['side-with-categories'],
'always-displayed' => $_['always-displayed'] && !$_['big-menu'] && !$_['side-with-categories'],
'side-with-categories' => $_['side-with-categories'] && !$_['always-displayed'] && !$_['big-menu'],
'big-menu' => $_['big-menu'] && !$_['always-displayed'] && !$_['side-with-categories'],
];
?>
<div>
<label>
<?php p($l->t('Default')); ?>
</label>
</div>
<p>
<img
class="side-menu-display <?php echo $displays['default'] ? 'is-active' : '' ?>"
data-alwaysdiplayed="0"
data-bigmenu="0"
data-sidewithcategories="0"
src="<?php print_unescaped(image_path('side_menu', 'admin/layout-default.svg')); ?>" alt="<?php p($l->t('Default')); ?>">
</p>
<div>
<label>
<?php p($l->t('With categories')); ?>
</label>
</div>
<p>
<img
class="side-menu-display <?php echo $displays['side-with-categories'] ? 'is-active' : '' ?>"
data-alwaysdiplayed="0"
data-bigmenu="0"
data-sidewithcategories="1"
src="<?php print_unescaped(image_path('side_menu', 'admin/layout-side-with-categories.svg')); ?>" alt="<?php p($l->t('With categories')); ?>">
</p>
<div>
<label for="side-menu-opener">
<?php p($l->t('Big menu')); ?>
</label>
</div>
<p>
<img
class="side-menu-display <?php echo $displays['big-menu'] ? 'is-active' : '' ?>"
data-alwaysdiplayed="0"
data-bigmenu="1"
data-sidewithcategories="0"
src="<?php print_unescaped(image_path('side_menu', 'admin/layout-big-menu.svg')); ?>" alt="<?php p($l->t('Big menu')); ?>">
</p>
<div>
<label for="side-menu-opener">
<?php p($l->t('Always displayed')); ?>
</label>
</div>
<p><em><?php p($l->t('Not compatible with touch screens.')); ?></em></p>
<p>
<img
class="side-menu-display <?php echo $displays['always-displayed'] ? 'is-active' : '' ?>"
data-alwaysdiplayed="1"
data-bigmenu="0"
data-sidewithcategories="0"
src="<?php print_unescaped(image_path('side_menu', 'admin/layout-always-displayed.svg')); ?>" alt="<?php p($l->t('Always displayed')); ?>">
</p>
<input
type="hidden"
class="side-menu-setting"
name="always-displayed"
id="side-menu-always-displayed"
value="<?php echo (int) $_['always-displayed'] ?>"
>
<input
type="hidden"
class="side-menu-setting"
name="big-menu"
id="side-menu-big-menu"
value="<?php echo (int) $_['big-menu'] ?>"
>
<input
type="hidden"
class="side-menu-setting"
name="side-with-categories"
id="side-menu-side-with-categories"
value="<?php echo (int) $_['side-with-categories'] ?>"
>
</div>
<div class="section"> <div class="section">
<h2> <h2>
<?php p($l->t('Colors')); ?> <?php p($l->t('Colors')); ?>
@@ -146,13 +50,6 @@ $labelReset = 'Reset to default';
<small><span class="info"><?php p($l->t('Live preview')); ?></span></small> <small><span class="info"><?php p($l->t('Live preview')); ?></span></small>
</h2> </h2>
<div class="badges">
<span class="badge badge-1"><?php p($l->t('Default')); ?></span>
<span class="badge badge-2"><?php p($l->t('With categories')); ?></span>
<span class="badge badge-3"><?php p($l->t('Big menu')); ?></span>
<span class="badge badge-4"><?php p($l->t('Always displayed')); ?></span>
</div>
<div class="side-menu-setting-table"> <div class="side-menu-setting-table">
<div class="side-menu-setting-row"> <div class="side-menu-setting-row">
<div class="side-menu-setting-label side-menu-setting-label-short"> <div class="side-menu-setting-label side-menu-setting-label-short">
@@ -337,13 +234,6 @@ $labelReset = 'Reset to default';
<?php p($l->t('Dark mode colors')); ?> <?php p($l->t('Dark mode colors')); ?>
</h2> </h2>
<div class="badges">
<span class="badge badge-1"><?php p($l->t('Default')); ?></span>
<span class="badge badge-2"><?php p($l->t('With categories')); ?></span>
<span class="badge badge-3"><?php p($l->t('Big menu')); ?></span>
<span class="badge badge-4"><?php p($l->t('Always displayed')); ?></span>
</div>
<p> <p>
<?php echo $l->t('This parameters are used when Dark theme or Breeze Dark Theme are enabled.'); ?> <?php echo $l->t('This parameters are used when Dark theme or Breeze Dark Theme are enabled.'); ?>
</p> </p>
@@ -532,13 +422,6 @@ $labelReset = 'Reset to default';
<?php p($l->t('Opener')); ?> <?php p($l->t('Opener')); ?>
</h2> </h2>
<div class="badges">
<span class="badge badge-1"><?php p($l->t('Default')); ?></span>
<span class="badge badge-2"><?php p($l->t('With categories')); ?></span>
<span class="badge badge-3"><?php p($l->t('Big menu')); ?></span>
<span class="badge badge-4"><?php p($l->t('Always displayed')); ?></span>
</div>
<div class="side-menu-setting-table"> <div class="side-menu-setting-table">
<div class="side-menu-setting-row"> <div class="side-menu-setting-row">
<?php <?php
@@ -590,7 +473,107 @@ $labelReset = 'Reset to default';
</select> </select>
</div> </div>
</div> </div>
</div>
</div>
<div class="section">
<h2>
<?php p($l->t('Panel')); ?>
</h2>
<?php
$displays = [
'default' => !$_['always-displayed'] && !$_['big-menu'] && !$_['side-with-categories'],
'always-displayed' => $_['always-displayed'] && !$_['big-menu'] && !$_['side-with-categories'],
'side-with-categories' => $_['side-with-categories'] && !$_['always-displayed'] && !$_['big-menu'],
'big-menu' => $_['big-menu'] && !$_['always-displayed'] && !$_['side-with-categories'],
];
?>
<div>
<label>
<?php p($l->t('Default')); ?>
</label>
</div>
<p>
<img
class="side-menu-display <?php echo $displays['default'] ? 'is-active' : '' ?>"
data-alwaysdiplayed="0"
data-bigmenu="0"
data-sidewithcategories="0"
src="<?php print_unescaped(image_path('side_menu', 'admin/layout-default.svg')); ?>" alt="<?php p($l->t('Default')); ?>">
</p>
<div>
<label>
<?php p($l->t('With categories')); ?>
</label>
</div>
<p><em><?php echo $l->t('This menu is not compatible with AppOrder.'); ?></em></p>
<p>
<img
class="side-menu-display <?php echo $displays['side-with-categories'] ? 'is-active' : '' ?>"
data-alwaysdiplayed="0"
data-bigmenu="0"
data-sidewithcategories="1"
src="<?php print_unescaped(image_path('side_menu', 'admin/layout-side-with-categories.svg')); ?>" alt="<?php p($l->t('With categories')); ?>">
</p>
<div>
<label for="side-menu-opener">
<?php p($l->t('Big menu')); ?>
</label>
</div>
<p><em><?php echo $l->t('This menu is not compatible with AppOrder.'); ?></em></p>
<p>
<img
class="side-menu-display <?php echo $displays['big-menu'] ? 'is-active' : '' ?>"
data-alwaysdiplayed="0"
data-bigmenu="1"
data-sidewithcategories="0"
src="<?php print_unescaped(image_path('side_menu', 'admin/layout-big-menu.svg')); ?>" alt="<?php p($l->t('Big menu')); ?>">
</p>
<div>
<label for="side-menu-opener">
<?php p($l->t('Always displayed')); ?>
</label>
</div>
<p><em><?php p($l->t('Not compatible with touch screens.')); ?></em></p>
<p>
<img
class="side-menu-display <?php echo $displays['always-displayed'] ? 'is-active' : '' ?>"
data-alwaysdiplayed="1"
data-bigmenu="0"
data-sidewithcategories="0"
src="<?php print_unescaped(image_path('side_menu', 'admin/layout-always-displayed.svg')); ?>" alt="<?php p($l->t('Always displayed')); ?>">
</p>
<input
type="hidden"
class="side-menu-setting"
name="always-displayed"
id="side-menu-always-displayed"
value="<?php echo (int) $_['always-displayed'] ?>"
>
<input
type="hidden"
class="side-menu-setting"
name="big-menu"
id="side-menu-big-menu"
value="<?php echo (int) $_['big-menu'] ?>"
>
<input
type="hidden"
class="side-menu-setting"
name="side-with-categories"
id="side-menu-side-with-categories"
value="<?php echo (int) $_['side-with-categories'] ?>"
>
<br>
<div class="side-menu-setting-table">
<div class="side-menu-setting-row"> <div class="side-menu-setting-row">
<div class="side-menu-setting-label"> <div class="side-menu-setting-label">
<?php p($l->t('Open the menu when the mouse is hover the opener (automatically disabled on touch screens)')); ?> <?php p($l->t('Open the menu when the mouse is hover the opener (automatically disabled on touch screens)')); ?>
@@ -608,18 +591,16 @@ $labelReset = 'Reset to default';
</select> </select>
</div> </div>
</div> </div>
</div>
</div>
<div class="section">
<div class="side-menu-setting-table">
<div class="side-menu-setting-row"> <div class="side-menu-setting-row">
<div class="side-menu-setting-label"> <div class="side-menu-setting-label">
<?php p($l->t('Display the logo')); ?> <?php p($l->t('Display the logo')); ?>
<br>
<div class="badges"> <em>
<span class="badge badge-1"><?php p($l->t('Default')); ?></span> <?php echo $l->t('This feature is not compatible with the <code>big menu</code> display.'); ?>
</div> <br>
<?php p($l->t('The logo will be hidden when the menu is always displayed.')); ?>
</em>
</div> </div>
<div class="side-menu-setting-form"> <div class="side-menu-setting-form">
<select id="side-menu-display-logo" name="display-logo" class="side-menu-setting"> <select id="side-menu-display-logo" name="display-logo" class="side-menu-setting">
@@ -635,10 +616,6 @@ $labelReset = 'Reset to default';
<div class="side-menu-setting-row"> <div class="side-menu-setting-row">
<div class="side-menu-setting-label"> <div class="side-menu-setting-label">
<?php p($l->t('Use the avatar instead of the logo')); ?> <?php p($l->t('Use the avatar instead of the logo')); ?>
<div class="badges">
<span class="badge badge-1"><?php p($l->t('Default')); ?></span>
</div>
</div> </div>
<div class="side-menu-setting-form"> <div class="side-menu-setting-form">
<select id="side-menu-use-avatar" name="use-avatar" class="side-menu-setting"> <select id="side-menu-use-avatar" name="use-avatar" class="side-menu-setting">
@@ -654,10 +631,6 @@ $labelReset = 'Reset to default';
<div class="side-menu-setting-row"> <div class="side-menu-setting-row">
<div class="side-menu-setting-label"> <div class="side-menu-setting-label">
<?php p($l->t('The logo is a link to the default app')); ?> <?php p($l->t('The logo is a link to the default app')); ?>
<div class="badges">
<span class="badge badge-1"><?php p($l->t('Default')); ?></span>
</div>
</div> </div>
<div class="side-menu-setting-form"> <div class="side-menu-setting-form">
<select id="side-menu-add-logo-link" name="add-logo-link" class="side-menu-setting"> <select id="side-menu-add-logo-link" name="add-logo-link" class="side-menu-setting">
@@ -673,11 +646,8 @@ $labelReset = 'Reset to default';
<div class="side-menu-setting-row"> <div class="side-menu-setting-row">
<div class="side-menu-setting-label"> <div class="side-menu-setting-label">
<?php p($l->t('Apps that should not be displayed in the menu')); ?> <?php p($l->t('Apps that should not be displayed in the menu')); ?>
<br>
<div class="badges"> <em><?php echo $l->t('This feature is only compatible with the <code>big menu</code> display.'); ?></em>
<span class="badge badge-2"><?php p($l->t('With categories')); ?></span>
<span class="badge badge-3"><?php p($l->t('Big menu')); ?></span>
</div>
</div> </div>
<div class="side-menu-setting-form"> <div class="side-menu-setting-form">
<a class="side-menu-toggler" data-target="#big-menu-hidden-apps" href="#_"> <a class="side-menu-toggler" data-target="#big-menu-hidden-apps" href="#_">
@@ -797,17 +767,10 @@ $labelReset = 'Reset to default';
<?php p($l->t('Top menu')); ?> <?php p($l->t('Top menu')); ?>
</h2> </h2>
<div class="badges">
<span class="badge badge-1"><?php p($l->t('Default')); ?></span>
<span class="badge badge-2"><?php p($l->t('With categories')); ?></span>
<span class="badge badge-3"><?php p($l->t('Big menu')); ?></span>
<span class="badge badge-4"><?php p($l->t('Always displayed')); ?></span>
</div>
<div class="side-menu-setting-table"> <div class="side-menu-setting-table">
<div class="side-menu-setting-row"> <div class="side-menu-setting-row">
<div class="side-menu-setting-label"> <div class="side-menu-setting-label">
<?php p($l->t('Applications kept in the top menu')); ?> <?php p($l->t('Apps only visible in the top menu')); ?>
</div> </div>
<div class="side-menu-setting-form"> <div class="side-menu-setting-form">
<a class="side-menu-toggler" data-target="#top-menu-apps" href="#_"> <a class="side-menu-toggler" data-target="#top-menu-apps" href="#_">
@@ -835,12 +798,12 @@ $labelReset = 'Reset to default';
</div> </div>
</div> </div>
</div> </div>
</div>
<div class="side-menu-setting-table">
<div class="side-menu-setting-row"> <div class="side-menu-setting-row">
<div class="side-menu-setting-label"> <div class="side-menu-setting-label">
<?php p($l->t('Applications kept in the top menu but also shown in side menu')); ?> <?php p($l->t('Apps visible in the top and side menus')); ?>
<br>
<em><?php p($l->t('These applications must be selected in the previous option.')); ?></em>
</div> </div>
<div class="side-menu-setting-form"> <div class="side-menu-setting-form">
<a class="side-menu-toggler" data-target="#top-side-menu-apps" href="#_"> <a class="side-menu-toggler" data-target="#top-side-menu-apps" href="#_">
@@ -871,63 +834,12 @@ $labelReset = 'Reset to default';
</div> </div>
</div> </div>
<div class="section">
<h2>
<?php p($l->t('Applications')); ?>
</h2>
<div class="badges">
<span class="badge badge-1"><?php p($l->t('Default')); ?></span>
<span class="badge badge-2"><?php p($l->t('With categories')); ?></span>
<span class="badge badge-3"><?php p($l->t('Big menu')); ?></span>
<span class="badge badge-4"><?php p($l->t('Always displayed')); ?></span>
</div>
<div class="side-menu-setting-table">
<div class="side-menu-setting-row">
<div class="side-menu-setting-label">
<?php p($l->t('Customize sorting')); ?>
</div>
<div class="side-menu-setting-form">
<a class="side-menu-toggler" data-target="#apps-order-list" href="#_">
🖱️ <?php p($l->t($labelShowHideApps)); ?>
</a>
<div class="theme-undo icon icon-history btn-reset btn-reset--down" data-toggle="tooltip" data-original-title="<?php echo p($l->t($labelReset)); ?>" data-reset="<?php echo htmlentities(json_encode([
'side-menu-apps-order' => [],
])) ?>"></div>
<div id="apps-order-list" style="display: none">
<ul class="side-menu-setting-list">
<?php foreach ($_['ordered-apps'] as $key => $app): ?>
<li data-id="<?php echo $app['id']; ?>" class="side-menu-setting-list-item">
<span class="arrow">
⇅
</span>
<?php echo p($l->t($app['name'])); ?>
</li>
<?php endforeach; ?>
</ul>
</div>
<input type="hidden" value='<?php echo json_encode($_['apps-order']) ?>' name="apps-order" class="side-menu-setting" id="side-menu-apps-order">
</div>
</div>
</div>
</div>
<div class="section"> <div class="section">
<h2> <h2>
<?php p($l->t('Categories')); ?> <?php p($l->t('Categories')); ?>
</h2> </h2>
<div class="badges">
<span class="badge badge-2"><?php p($l->t('With categories')); ?></span>
<span class="badge badge-3"><?php p($l->t('Big menu')); ?></span>
</div>
<div class="side-menu-setting-table"> <div class="side-menu-setting-table">
<div class="side-menu-setting-row"> <div class="side-menu-setting-row">
<?php <?php
@@ -1038,13 +950,6 @@ $labelReset = 'Reset to default';
<?php p($l->t('Tips')); ?> <?php p($l->t('Tips')); ?>
</h2> </h2>
<div class="badges">
<span class="badge badge-1"><?php p($l->t('Default')); ?></span>
<span class="badge badge-2"><?php p($l->t('With categories')); ?></span>
<span class="badge badge-3"><?php p($l->t('Big menu')); ?></span>
<span class="badge badge-4"><?php p($l->t('Always displayed')); ?></span>
</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>
+6 -42
View File
@@ -27,7 +27,6 @@ $choicesYesNo = [
$labelShowHideApps = 'Show and hide the list of applications'; $labelShowHideApps = 'Show and hide the list of applications';
$labelReset = 'Reset to default';
?> ?>
<div id="side-menu-section"> <div id="side-menu-section">
<?php if ($_['force']): ?> <?php if ($_['force']): ?>
@@ -42,6 +41,10 @@ $labelReset = 'Reset to default';
</div> </div>
<?php else: ?> <?php else: ?>
<div class="section"> <div class="section">
<h2>
<?php p($l->t('Menu')); ?>
</h2>
<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>
@@ -120,7 +123,7 @@ $labelReset = 'Reset to default';
<div class="side-menu-setting-table"> <div class="side-menu-setting-table">
<div class="side-menu-setting-row"> <div class="side-menu-setting-row">
<div class="side-menu-setting-label"> <div class="side-menu-setting-label">
<?php p($l->t('Applications kept in the top menu')); ?> <?php p($l->t('Apps only visible in the top menu')); ?>
<p> <p>
<em> <em>
<?php p($l->t('If there is no selection then the global configuration is applied.')); ?> <?php p($l->t('If there is no selection then the global configuration is applied.')); ?>
@@ -160,10 +163,9 @@ $labelReset = 'Reset to default';
<div class="side-menu-setting-table"> <div class="side-menu-setting-table">
<div class="side-menu-setting-row"> <div class="side-menu-setting-row">
<div class="side-menu-setting-label"> <div class="side-menu-setting-label">
<?php p($l->t('Applications kept in the top menu but also shown in side menu')); ?> <?php p($l->t('Apps visible in the top and side menus')); ?>
<p> <p>
<em> <em>
<?php p($l->t('These applications must be selected in the previous option.')); ?><br>
<?php p($l->t('If there is no selection then the global configuration is applied.')); ?> <?php p($l->t('If there is no selection then the global configuration is applied.')); ?>
</em> </em>
</p> </p>
@@ -198,44 +200,6 @@ $labelReset = 'Reset to default';
</div> </div>
</div> </div>
</div> </div>
<div class="section">
<h2>
<?php p($l->t('Applications')); ?>
</h2>
<div class="side-menu-setting-table">
<div class="side-menu-setting-row">
<div class="side-menu-setting-label">
<?php p($l->t('Customize sorting')); ?>
</div>
<div class="side-menu-setting-form">
<a class="side-menu-toggler" data-target="#apps-order-list" href="#_">
🖱️ <?php p($l->t($labelShowHideApps)); ?>
</a>
<div class="theme-undo icon icon-history btn-reset btn-reset--down" data-toggle="tooltip" data-original-title="<?php echo p($l->t($labelReset)); ?>" data-reset="<?php echo htmlentities(json_encode([
'side-menu-apps-order' => '[]',
])) ?>"></div>
<div id="apps-order-list" style="display: none">
<ul class="side-menu-setting-list">
<?php foreach ($_['ordered-apps'] as $key => $app): ?>
<li data-id="<?php echo $app['id']; ?>" class="side-menu-setting-list-item">
<span class="arrow">
⇅
</span>
<?php echo p($l->t($app['name'])); ?>
</li>
<?php endforeach; ?>
</ul>
</div>
<input type="hidden" value='<?php echo json_encode($_['apps-order']) ?>' name="apps-order" class="side-menu-setting" id="side-menu-apps-order" data-personal>
</div>
</div>
</div>
</div>
<?php endif ?> <?php endif ?>
<div class="section"> <div class="section">
-1
View File
@@ -3,7 +3,6 @@ const { VueLoaderPlugin } = require('vue-loader')
const StyleLintPlugin = require('stylelint-webpack-plugin') const StyleLintPlugin = require('stylelint-webpack-plugin')
module.exports = { module.exports = {
devtool: "source-map",
entry: { entry: {
'admin': path.join(__dirname, 'src', 'admin.js'), 'admin': path.join(__dirname, 'src', 'admin.js'),
'sideMenu': path.join(__dirname, 'src', 'SideMenu.js'), 'sideMenu': path.join(__dirname, 'src', 'SideMenu.js'),