Merge branch 'develop' into translations
All checks were successful
ci/woodpecker/pr/woodpecker Pipeline was successful
ci/woodpecker/push/woodpecker Pipeline was successful

This commit is contained in:
Simon Vieille 2023-11-05 18:40:51 +01:00
commit a18732b043
26 changed files with 386 additions and 92 deletions

View file

@ -1,47 +1,78 @@
pipeline: steps:
dependencies: dependencies:
image: gitnet.fr/deblan/devenv image: node:16
pull: true
commands: commands:
- make dep - npm i
when: when:
event: [tag, push, pull_request] event: [tag, push, pull_request, manual]
branch: [master, develop, feature/*, translations] branch: [master, develop, feature/*, fix/*, bugfix/*, translations]
osv_detector: osv-detector:
image: gitnet.fr/deblan/docker-osv-detector:v0.9 image: gitnet.fr/deblan/osv-detector:v0.10
commands: commands:
- osv-detector package-lock.json - osv-detector package-lock.json
failure: ignore failure: ignore
build: build-js:
image: gitnet.fr/deblan/devenv image: node:16
commands: commands:
- make build - npm run build
when: when:
branch: [master, develop, feature/*, translations] event: [tag, push, pull_request, manual]
event: [push, pull_request] branch: [master, develop, feature/*, fix/*, bugfix/*, translations]
code_quality: build-translations:
image: sonarsource/sonar-scanner-cli image: deblan/php:8.0
secrets: [sonar_token, sonar_host, sonar_project]
commands: commands:
- sonar-scanner - php bin/generate_l10n.php
-Dsonar.projectKey=$SONAR_PROJECT
-Dsonar.sources=.
-Dsonar.host.url=$SONAR_HOST
-Dsonar.pullrequest.key=$CI_COMMIT_PULL_REQUEST
-Dsonar.pullrequest.branch=$CI_COMMIT_SOURCE_BRANCH
-Dsonar.pullrequest.base=$CI_COMMIT_TARGET_BRANCH
failure: ignore
when: when:
event: [pull_request] event: [tag, push, pull_request, manual]
branch: [master, develop, feature/*, fix/*, bugfix/*, translations]
package: create-signature:
image: gitnet.fr/deblan/devenv image: nextcloud:25
secrets: [app_certificate, app_public_certificate]
environment:
SQLITE_DATABASE: /var/www/data/data.db
NEXTCLOUD_ADMIN_USER: admin
NEXTCLOUD_ADMIN_PASSWORD: admin
commands:
- echo "$APP_CERTIFICATE" > "/tmp/side_menu.key"
- echo "$APP_PUBLIC_CERTIFICATE" > "/tmp/side_menu.crt"
- mkdir /tmp/app
- cp -r README.md CHANGELOG.md appinfo css lib img l10n js src templates screenshots vendor /tmp/app
- /usr/src/nextcloud/occ integrity:sign-app
--privateKey=/tmp/side_menu.key
--certificate=/tmp/side_menu.crt
--path=/tmp/app
- mv /tmp/app/appinfo/signature.json appinfo/
when:
event: [tag]
# check-code-quality:
# image: sonarsource/sonar-scanner-cli
# secrets: [sonar_token, sonar_host, sonar_project]
# commands:
# - sonar-scanner
# -Dsonar.projectKey=$SONAR_PROJECT
# -Dsonar.sources=.
# -Dsonar.host.url=$SONAR_HOST
# -Dsonar.pullrequest.key=$CI_COMMIT_PULL_REQUEST
# -Dsonar.pullrequest.branch=$CI_COMMIT_SOURCE_BRANCH
# -Dsonar.pullrequest.base=$CI_COMMIT_TARGET_BRANCH
# failure: ignore
# when:
# event: [pull_request]
create-package:
image: deblan/php:8.0
volumes: volumes:
- /var/www/html/artifacts:/var/www/html/artifacts - /var/www/html/artifacts:/var/www/html/artifacts
secrets: [app_certificate] secrets: [app_certificate]
commands: commands:
- apt-get update
- apt-get install -y zip make
- mkdir -p "$HOME/.nextcloud/certificates" - mkdir -p "$HOME/.nextcloud/certificates"
- echo "$APP_CERTIFICATE" > "$HOME/.nextcloud/certificates/side_menu.key" - echo "$APP_CERTIFICATE" > "$HOME/.nextcloud/certificates/side_menu.key"
- export VERSION=$(grep "<version>" appinfo/info.xml | grep -o "[0-9]*\.[0-9]*\.[0-9]*" --color=never) - export VERSION=$(grep "<version>" appinfo/info.xml | grep -o "[0-9]*\.[0-9]*\.[0-9]*" --color=never)
@ -50,7 +81,7 @@ pipeline:
when: when:
event: [tag] event: [tag]
release: push-release:
image: plugins/gitea-release image: plugins/gitea-release
volumes: volumes:
- /var/www/html/artifacts:/var/www/html/artifacts - /var/www/html/artifacts:/var/www/html/artifacts

View file

@ -1,5 +1,64 @@
## [Unreleased] ## [Unreleased]
## 3.11.0
### Added
* add a search component in menus
### Fixed
* remove the label of the link to personal settings - fix #283
## 3.10.3
### Fixed
* change the way to load nextcloud components (NcActionLink/NcActions) - fix #274
* update @nexcloud/* packages
## 3.10.2
### Fixed
* add missing properties
## 3.10.1
### Fixed
* fix #269: use php7 syntax
## 3.10.0
### Added
* add compatibility with NC28
### Fixed
* fix NC28 error: remove deprecated method `OC_App::getNavigation()`
## 3.9.1
### Fixed
* fix fixed menu on dashboard (#262)
## 3.9.0
### Added
* add compatibility with NC27
### Fixed
* fix app redirect (#261)
## 3.8.0
### Added
* add option to show hovered label only on top menu (fix #253)
## 3.7.4
### Fixed
* fix Integrity failed (#247)
## 3.7.3
### Fixed
* fix #244: use app href for redirection
### Added
* add signature on build
## 3.7.2
### Added
* update pipeline conditions allowing `fix/*`
### Fixed
* fix #233: load configuration and then retrieve apps in default side menu display
## 3.7.1
### Fixed
* fix build process (#230)
## 3.7.0 ## 3.7.0
### Added ### Added
* add translations (thanks to AHOHNMYC) * add translations (thanks to AHOHNMYC)

View file

@ -9,7 +9,7 @@ dep:
npm link @nextcloud/vue || sudo npm link @nextcloud/vue npm link @nextcloud/vue || sudo npm link @nextcloud/vue
.ONESHELL: .ONESHELL:
release: build translations release:
if [ -z "$$VERSION" ]; then if [ -z "$$VERSION" ]; then
echo "VERSION required" echo "VERSION required"
exit 1 exit 1

View file

@ -3,6 +3,7 @@
[![Build Status](https://ci.gitnet.fr/api/badges/deblan/side_menu/status.svg)](https://ci.gitnet.fr/deblan/side_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/) [![Translations](https://translate.codeberg.org/widgets/custom-menu/-/application/svg-badge.svg)](https://translate.codeberg.org/engage/custom-menu/)
![Downloads](https://img.shields.io/badge/dynamic/json?color=brightgreen&label=downloads&query=%24.K_downloads&suffix=K&url=https%3A%2F%2Fapi-side-menu.deblan.org%2Fdownloads.php)
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.

View file

@ -32,7 +32,7 @@ Notice
Because I believe in a free and decentralized Internet, [Gitnet](https://gitnet.fr) is **self-hosted at home**. 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/). In case of downtime, you can download **Custom Menu** from [here](https://kim.deblan.fr/~side_menu/).
]]></description> ]]></description>
<version>3.7.0</version> <version>3.11.0</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>
@ -54,7 +54,7 @@ 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/nc25_big_menu.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> <screenshot>https://gitnet.fr/deblan/side_menu/raw/branch/master/screenshots/nc25_default_menu.png</screenshot>
<dependencies> <dependencies>
<nextcloud min-version="25" max-version="26"/> <nextcloud min-version="25" max-version="28"/>
<php min-version="7.4"/> <php min-version="7.4"/>
</dependencies> </dependencies>
<settings> <settings>

View file

@ -47,7 +47,7 @@
margin-top: 2px; margin-top: 2px;
float: right; float: right;
line-height: 34px; line-height: 34px;
height: 28px; height: 42px;
display: none; display: none;
} }
@ -155,7 +155,7 @@
} }
#side-menu.hide-opener .side-menu-logo { #side-menu.hide-opener .side-menu-logo {
margin-top: 20px; margin-top: 10px;
} }
#side-menu-loader { #side-menu-loader {
@ -223,7 +223,7 @@
} }
.side-menu-loader svg { .side-menu-loader svg {
width: 38px; width: 45px;
margin: auto; margin: auto;
stroke: var(--side-menu-text-color, #fff); stroke: var(--side-menu-text-color, #fff);
} }
@ -239,14 +239,23 @@
left: 50px; left: 50px;
} }
.side-menu-always-displayed #header {
position: absolute !important;
}
.side-menu-always-displayed #side-menu { .side-menu-always-displayed #side-menu {
display: block; display: block;
} }
.side-menu-always-displayed .side-menu-apps-list { .side-menu-always-displayed .side-menu-apps-list {
height: 100vh;
top: 0;
overflow: hidden;
}
.side-menu-always-displayed .side-menu-apps-list--with-settings {
height: calc(100vh - 49px); height: calc(100vh - 49px);
top: 49px; top: 49px;
overflow: hidden;
} }
.side-menu-always-displayed .side-menu-apps-list:hover { .side-menu-always-displayed .side-menu-apps-list:hover {
@ -309,12 +318,35 @@
visibility: visible; visibility: visible;
} }
.side-menu-search {
float: right;
}
.side-menu-search input {
background: none;
border: 0;
border-radius: 0;
color: var(--side-menu-text-color);
}
.side-menu-search input::placeholder {
color: var(--side-menu-text-color);
}
.side-menu-always-displayed .side-menu-search {
display: none;
}
@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;
height: 100vh; height: 100vh;
} }
#side-menu.hide-opener.side-menu-big .side-menu-search {
float: none;
}
.side-menu-categories { .side-menu-categories {
display: block; display: block;
padding: 0; padding: 0;

View file

@ -74,33 +74,37 @@ class AppController extends Controller
$inTopMenuApps = in_array($app['id'], $topMenuApps); $inTopMenuApps = in_array($app['id'], $topMenuApps);
$inHiddenApps = in_array($app['id'], $hiddenApps); $inHiddenApps = in_array($app['id'], $hiddenApps);
if (!$inTopMenuApps || $inHiddenApps) { if (!$inTopMenuApps && $inHiddenApps) {
continue; continue;
} }
return $this->redirectToApp($app['id']); return $this->redirectToApp($app, true);
} }
return $this->redirectToApp('files'); return $this->redirectToApp('files');
} }
protected function redirectToApp($appId): RedirectResponse protected function redirectToApp($app, bool $isHref = false): RedirectResponse
{ {
$isIgnoreFrontController = true === OC::$server->getConfig()->getSystemValue( if (!$isHref) {
'htaccess.IgnoreFrontController', $isIgnoreFrontController = true === OC::$server->getConfig()->getSystemValue(
false 'htaccess.IgnoreFrontController',
); false
);
$isFrontControllerActive = 'true' === getenv('front_controller_active'); $isFrontControllerActive = 'true' === getenv('front_controller_active');
if ($isIgnoreFrontController || $isFrontControllerActive) { if ($isIgnoreFrontController || $isFrontControllerActive) {
$path = '/apps/%s/'; $path = '/apps/%s/';
} else {
$path = '/index.php/apps/%s/';
}
$url = $this->urlGenerator->getAbsoluteURL(sprintf($path, $app));
} else { } else {
$path = '/index.php/apps/%s/'; $url = $app['href'];
} }
$url = $this->urlGenerator->getAbsoluteURL(sprintf($path, $appId));
return new RedirectResponse($url); return new RedirectResponse($url);
} }
} }

View file

@ -171,7 +171,7 @@ class JsController extends Controller
'avatar' => $avatar, 'avatar' => $avatar,
'top-menu-apps' => $topMenuApps, 'top-menu-apps' => $topMenuApps,
'top-side-menu-apps' => $topSideMenuApps, 'top-side-menu-apps' => $topSideMenuApps,
'top-menu-mouse-over-hidden-label' => $this->config->getAppValueBool( 'top-menu-mouse-over-hidden-label' => $this->config->getAppValueInt(
'top-menu-mouse-over-hidden-label', 'top-menu-mouse-over-hidden-label',
'0' '0'
), ),

View file

@ -3,6 +3,7 @@
namespace OCA\SideMenu\Service; namespace OCA\SideMenu\Service;
use OC\User\User; use OC\User\User;
use OCP\INavigationManager;
use OCP\L10N\IFactory; use OCP\L10N\IFactory;
/** /**
@ -32,8 +33,14 @@ class AppRepository
*/ */
protected $categoryRepository; protected $categoryRepository;
/**
* @var INavigationManager
*/
protected $navigationManager;
public function __construct( public function __construct(
\OC_App $ocApp, \OC_App $ocApp,
INavigationManager $navigationManager,
IFactory $l10nFactory, IFactory $l10nFactory,
ConfigProxy $config, ConfigProxy $config,
CategoryRepository $categoryRepository CategoryRepository $categoryRepository
@ -41,6 +48,7 @@ class AppRepository
$this->ocApp = $ocApp; $this->ocApp = $ocApp;
$this->l10nFactory = $l10nFactory; $this->l10nFactory = $l10nFactory;
$this->config = $config; $this->config = $config;
$this->navigationManager = $navigationManager;
$this->categoryRepository = $categoryRepository; $this->categoryRepository = $categoryRepository;
} }
@ -51,7 +59,7 @@ class AppRepository
*/ */
public function getVisibleApps() public function getVisibleApps()
{ {
$navigation = $this->ocApp->getNavigation(); $navigation = $this->navigationManager->getAll();
$appCategoriesCustom = $this->config->getAppValueArray('apps-categories-custom', '[]'); $appCategoriesCustom = $this->config->getAppValueArray('apps-categories-custom', '[]');
$categories = $this->categoryRepository->getOrderedCategories(); $categories = $this->categoryRepository->getOrderedCategories();
$apps = $this->ocApp->listAllApps(); $apps = $this->ocApp->listAllApps();

View file

@ -25,12 +25,12 @@
"@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/axios": "^2.3.0",
"@nextcloud/browserslist-config": "^1.0.0", "@nextcloud/browserslist-config": "^2.3.0",
"@nextcloud/eslint-config": "^8.1.2", "@nextcloud/eslint-config": "^8.1.2",
"@nextcloud/initial-state": "^2.0.0", "@nextcloud/initial-state": "^2.0.0",
"@nextcloud/l10n": "^1.6.0", "@nextcloud/l10n": "^2.1.0",
"@nextcloud/vue": "^7.0.0", "@nextcloud/vue": "^7.12.1",
"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",

View file

@ -24,14 +24,14 @@
<nav class="app-menu show"> <nav class="app-menu show">
<ul <ul
class="app-menu-main" class="app-menu-main"
:class="{ 'app-menu-main__hidden-label': hiddenLabels }" :class="{ 'app-menu-main__hidden-label': hiddenLabels === 1, 'app-menu-main__show-hovered': hiddenLabels === 2 }"
v-if="apps !== null" v-if="apps !== null"
> >
<li v-for="app in mainAppList()" <li v-for="app in mainAppList()"
:key="app.id" :key="app.id"
:data-app-id="app.id" :data-app-id="app.id"
class="app-menu-entry" class="app-menu-entry"
:class="{ 'app-menu-entry__active': app.active, 'app-menu-entry__hidden-label': hiddenLabels }" :class="{ 'app-menu-entry__active': app.active, 'app-menu-entry__hidden-label': hiddenLabels === 1, 'app-menu-main__show-hovered': hiddenLabels === 2 }"
:style="makeStyle(app)" :style="makeStyle(app)"
> >
<a :href="app.href" <a :href="app.href"
@ -69,7 +69,8 @@
<script> <script>
import { loadState } from '@nextcloud/initial-state' import { loadState } from '@nextcloud/initial-state'
import { NcActions, NcActionLink } from '@nextcloud/vue' import NcActions from '@nextcloud/vue/dist/Components/NcActions.js'
import NcActionLink from '@nextcloud/vue/dist/Components/NcActionLink.js'
export default { export default {
name: 'AppMenu', name: 'AppMenu',
@ -233,8 +234,8 @@ $header-icon-size: 20px;
overflow: hidden; overflow: hidden;
} }
&:not(.app-menu-entry__hidden-label):hover, &:not(.app-menu-entry__hidden-label):not(.app-menu-entry__show-hovered):hover,
&:not(.app-menu-entry__hidden-label):focus-within { &:not(.app-menu-entry__hidden-label):not(.app-menu-entry__show-hovered):focus-within {
opacity: 1; opacity: 1;
.app-menu-entry--label { .app-menu-entry--label {
opacity: 1; opacity: 1;
@ -245,7 +246,6 @@ $header-icon-size: 20px;
overflow: visible; overflow: visible;
} }
} }
} }
// Show labels // Show labels
@ -256,8 +256,8 @@ $header-icon-size: 20px;
opacity: 1; opacity: 1;
} }
&:not(.app-menu-main__hidden-label):hover, &:not(.app-menu-main__hidden-label):not(.app-menu-main__show-hovered):hover,
&:not(.app-menu-main__hidden-label):focus-within, &:not(.app-menu-main__hidden-label):not(.app-menu-main__show-hovered):focus-within,
.app-menu-entry:not(.app-menu-entry__hidden-label):hover, .app-menu-entry:not(.app-menu-entry__hidden-label):hover,
.app-menu-entry:not(.app-menu-entry__hidden-label):focus { .app-menu-entry:not(.app-menu-entry__hidden-label):focus {
img { img {
@ -273,6 +273,22 @@ $header-icon-size: 20px;
opacity: 0; opacity: 0;
} }
} }
&.app-menu-main__show-hovered .app-menu-entry:hover,
&.app-menu-main__show-hovered .app-menu-entry:focus {
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 { ::v-deep .app-menu-more .button-vue--vue-tertiary {

32
src/AppSearch.vue Normal file
View file

@ -0,0 +1,32 @@
<!--
@license GNU AGPL version 3 or any later version
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
-->
<template>
<div class="side-menu-search">
<input type="text" :value="value" :placeholder="t('side_menu', 'Search')" @input="$emit('input', $event.target.value)">
</div>
</template>
<script>
export default {
name: 'AppSearch',
props: {
value: {
required: true
},
},
}
</script>

View file

@ -17,7 +17,9 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
<template> <template>
<div class="side-menu-settings"> <div class="side-menu-settings">
<a v-bind:href="href"> <a v-bind:href="href">
<!--
{{ label }} {{ label }}
-->
<span class="avatardiv avatardiv-shown"> <span class="avatardiv avatardiv-shown">
<img v-bind:src="avatar" :alt="label"> <img v-bind:src="avatar" :alt="label">

View file

@ -16,21 +16,19 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
--> -->
<template> <template>
<div id="side-menu"> <div id="side-menu">
<div class="side-menu-header"> <div class="side-menu-header" v-if="settings || !openerHover || (!avatar && !alwaysDisplayed && logo) || avatar">
<SettingsButton <SettingsButton
v-if="settings" v-if="settings"
v-bind:href="settings.href" v-bind:href="settings.href"
v-bind:label="settings.name" v-bind:label="settings.name"
v-bind:avatar="settings.avatar" /> v-bind:avatar="settings.avatar" />
<AppSearch v-model:search="search" />
<OpenerButton /> <OpenerButton />
<Logo <Logo
v-if="!avatar && logo" v-bind:classes="{'side-menu-logo': true, 'avatardiv': false}" v-if="!avatar && !alwaysDisplayed && logo" v-bind:classes="{'side-menu-logo': true, 'avatardiv': false}"
v-bind:image="logo" v-bind:image="logo"
v-bind:link="logoLink" v-bind:link="logoLink"
/> />
<Logo <Logo
v-if="avatar" v-bind:classes="{'side-menu-logo': true, 'avatardiv': true}" v-if="avatar" v-bind:classes="{'side-menu-logo': true, 'avatardiv': true}"
v-bind:image="avatar" v-bind:image="avatar"
@ -38,10 +36,10 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
/> />
</div> </div>
<ul class="side-menu-apps-list"> <ul class="side-menu-apps-list" :class="{'side-menu-apps-list--with-settings': !!settings}">
<SideMenuApp <SideMenuApp
v-for="(app, key) in apps" v-for="(app, key) in apps"
v-if="!hiddenApps.includes(app.id)" v-if="!hiddenApps.includes(app.id) && searchMatch(app.name)"
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:key="key"
v-bind:icon="app.icon" v-bind:icon="app.icon"
@ -58,6 +56,7 @@ import axios from 'axios'
import OpenerButton from './OpenerButton' import OpenerButton from './OpenerButton'
import SettingsButton from './SettingsButton' import SettingsButton from './SettingsButton'
import SideMenuApp from './SideMenuApp' import SideMenuApp from './SideMenuApp'
import AppSearch from './AppSearch'
import Logo from './Logo' import Logo from './Logo'
import { loadState } from '@nextcloud/initial-state' import { loadState } from '@nextcloud/initial-state'
@ -68,6 +67,7 @@ export default {
OpenerButton, OpenerButton,
SideMenuApp, SideMenuApp,
Logo, Logo,
AppSearch,
}, },
data() { data() {
return { return {
@ -79,6 +79,9 @@ export default {
targetBlankApps: [], targetBlankApps: [],
hiddenApps: [], hiddenApps: [],
settings: null, settings: null,
openerHover: false,
alwaysDisplayed: false,
search: '',
} }
}, },
methods: { methods: {
@ -93,7 +96,11 @@ export default {
for (let id in ncApps) { for (let id in ncApps) {
if (window.topMenuApps.includes(id) && !window.topSideMenuApps.includes(id)) { if (window.topMenuApps.includes(id) && !window.topSideMenuApps.includes(id)) {
continue; continue
}
if (this.hiddenApps.includes(id)) {
continue
} }
let app = ncApps[id] let app = ncApps[id]
@ -104,10 +111,10 @@ export default {
finalApps.sort((a, b) => { finalApps.sort((a, b) => {
if (a.order === null || b.order === null) { if (a.order === null || b.order === null) {
return a.name < b.name ? -1 : 1; return a.name < b.name ? -1 : 1
} }
return a.order < b.order ? -1 : 1; return a.order < b.order ? -1 : 1
}) })
this.apps = finalApps this.apps = finalApps
@ -118,24 +125,47 @@ export default {
}, },
retrieveConfig() { retrieveConfig() {
axios },
.get(OC.generateUrl('/apps/side_menu/js/config'))
.then((response) => {
const config = response.data
this.targetBlankApps = config['target-blank-apps'] hasSearchMatch(apps) {
this.forceLightIcon = config['force-light-icon'] if (this.search.trim() === '') {
this.avatar = config['avatar'] return true
this.logo = config['logo'] }
this.logoLink = config['logo-link']
this.settings = config['settings'] for (let key in apps) {
this.hiddenApps = config['big-menu-hidden-apps'] if (this.searchMatch(apps[key].name)) {
}) return true
}
}
return false
},
searchMatch(name) {
if (this.search.trim() === '') {
return true
}
return name.toLowerCase().includes(this.search.toLowerCase())
}, },
}, },
mounted() { mounted() {
this.retrieveConfig() axios
this.retrieveApps() .get(OC.generateUrl('/apps/side_menu/js/config'))
.then((response) => {
const config = response.data
this.targetBlankApps = config['target-blank-apps']
this.forceLightIcon = config['force-light-icon']
this.avatar = config['avatar']
this.logo = config['logo']
this.logoLink = config['logo-link']
this.settings = config['settings']
this.openerHover = config['opener-hover']
this.alwaysDisplayed = config['always-displayed']
this.hiddenApps = config['big-menu-hidden-apps']
this.retrieveApps()
})
} }
} }
</script> </script>

View file

@ -18,14 +18,13 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
<div id="side-menu" class="side-menu-big"> <div id="side-menu" class="side-menu-big">
<div class="side-menu-header"> <div class="side-menu-header">
<CloserButton /> <CloserButton />
<SettingsButton <SettingsButton
v-if="settings" v-if="settings"
v-bind:href="settings.href" v-bind:href="settings.href"
v-bind:label="settings.name" v-bind:label="settings.name"
v-bind:avatar="settings.avatar" v-bind:avatar="settings.avatar"
/> />
<AppSearch v-model:search="search" />
<OpenerButton /> <OpenerButton />
</div> </div>
@ -33,12 +32,13 @@ 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, key) in items" v-if="hasSearchMatch(category.apps)" v-bind:key="key">
<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-if="searchMatch(app.name)"
v-bind:key="appId" 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"
@ -59,6 +59,7 @@ import OpenerButton from './OpenerButton'
import CloserButton from './CloserButton' import CloserButton from './CloserButton'
import SettingsButton from './SettingsButton' import SettingsButton from './SettingsButton'
import Loader from './Loader' import Loader from './Loader'
import AppSearch from './AppSearch'
import SideMenuBigApp from './SideMenuBigApp' import SideMenuBigApp from './SideMenuBigApp'
import { loadState } from '@nextcloud/initial-state' import { loadState } from '@nextcloud/initial-state'
@ -70,6 +71,7 @@ export default {
CloserButton, CloserButton,
Loader, Loader,
SideMenuBigApp, SideMenuBigApp,
AppSearch,
}, },
data() { data() {
return { return {
@ -78,6 +80,7 @@ export default {
targetBlank: false, targetBlank: false,
targetBlankApps: [], targetBlankApps: [],
settings: null, settings: null,
search: '',
} }
}, },
methods: { methods: {
@ -120,6 +123,28 @@ export default {
this.settings = config['settings'] this.settings = config['settings']
}) })
}, },
hasSearchMatch(apps) {
if (this.search.trim() === '') {
return true
}
for (let key in apps) {
if (this.searchMatch(apps[key].name)) {
return true
}
}
return false
},
searchMatch(name) {
if (this.search.trim() === '') {
return true
}
return name.toLowerCase().includes(this.search.toLowerCase())
},
}, },
mounted() { mounted() {
this.retrieveConfig() this.retrieveConfig()

View file

@ -23,7 +23,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
v-bind:label="settings.name" v-bind:label="settings.name"
v-bind:avatar="settings.avatar" v-bind:avatar="settings.avatar"
/> />
<AppSearch v-model:search="search" />
<OpenerButton /> <OpenerButton />
</div> </div>
@ -31,12 +31,13 @@ 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, key) in items" v-if="hasSearchMatch(category.apps)" v-bind:key="key">
<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-if="searchMatch(app.name)"
v-bind:key="appId" 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"
@ -56,6 +57,7 @@ import axios from 'axios'
import OpenerButton from './OpenerButton' import OpenerButton from './OpenerButton'
import SettingsButton from './SettingsButton' import SettingsButton from './SettingsButton'
import Loader from './Loader' import Loader from './Loader'
import AppSearch from './AppSearch'
import SideMenuBigApp from './SideMenuBigApp' import SideMenuBigApp from './SideMenuBigApp'
import { loadState } from '@nextcloud/initial-state' import { loadState } from '@nextcloud/initial-state'
@ -66,6 +68,7 @@ export default {
OpenerButton, OpenerButton,
Loader, Loader,
SideMenuBigApp, SideMenuBigApp,
AppSearch,
}, },
data() { data() {
return { return {
@ -74,6 +77,7 @@ export default {
targetBlank: false, targetBlank: false,
targetBlankApps: [], targetBlankApps: [],
settings: null, settings: null,
search: '',
} }
}, },
methods: { methods: {
@ -116,6 +120,28 @@ export default {
this.settings = config['settings'] this.settings = config['settings']
}) })
}, },
hasSearchMatch(apps) {
if (this.search.trim() === '') {
return true
}
for (let key in apps) {
if (this.searchMatch(apps[key].name)) {
return true
}
}
return false
},
searchMatch(name) {
if (this.search.trim() === '') {
return true
}
return name.toLowerCase().includes(this.search.toLowerCase())
},
}, },
mounted() { mounted() {
this.retrieveConfig() this.retrieveConfig()

View file

@ -91,3 +91,5 @@
"Applications kept in the top menu but also shown in side menu": "Aplikace ponechané v horní nabídce ale také zobrazené v té boční" "Applications kept in the top menu but also shown in side menu": "Aplikace ponechané v horní nabídce ale také zobrazené v té boční"
"These applications must be selected in the previous option.": "Tyto aplikace je třeba vybrat v předchozí volbě." "These applications must be selected in the previous option.": "Tyto aplikace je třeba vybrat v předchozí volbě."
"Hide labels on mouse over": "Skrýt popisky při najetím ukazatele myši" "Hide labels on mouse over": "Skrýt popisky při najetím ukazatele myši"
"Except the hovered app": "Except the hovered app"
"Search": "Search"

View file

@ -91,3 +91,5 @@
"Applications kept in the top menu but also shown in side menu": "Apps in der oberen Navigationsleiste, die auch im Seitenmenü angezeigt werden sollen" "Applications kept in the top menu but also shown in side menu": "Apps in der oberen Navigationsleiste, die auch im Seitenmenü angezeigt werden sollen"
"These applications must be selected in the previous option.": "Diese Apps müssen auch in der vorherigen Einstellung ausgewählt werden." "These applications must be selected in the previous option.": "Diese Apps müssen auch in der vorherigen Einstellung ausgewählt werden."
"Hide labels on mouse over": "Labels ausblenden, wenn sich die Maus darüber befindet (Hover)" "Hide labels on mouse over": "Labels ausblenden, wenn sich die Maus darüber befindet (Hover)"
"Except the hovered app": "Except the hovered app"
"Search": "Search"

View file

@ -91,3 +91,5 @@
"Applications kept in the top menu but also shown in side menu": "Las aplicaciones se mantienen en el menú superior pero también se muestran en el menú lateral" "Applications kept in the top menu but also shown in side menu": "Las aplicaciones se mantienen en el menú superior pero también se muestran en el menú lateral"
"These applications must be selected in the previous option.": "Estas aplicaciones deben ser seleccionadas en las opciones anteriores." "These applications must be selected in the previous option.": "Estas aplicaciones deben ser seleccionadas en las opciones anteriores."
"Hide labels on mouse over": "Ocultar las etiquetas al pasar el ratón" "Hide labels on mouse over": "Ocultar las etiquetas al pasar el ratón"
"Except the hovered app": "Except the hovered app"
"Search": "Search"

View file

@ -91,3 +91,5 @@
"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" "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." "These applications must be selected in the previous option.": "Ces applications doivent également être sélectionnées dans l'option précédente."
"Hide labels on mouse over": "Masquer le libellé des applications au passage de la souris" "Hide labels on mouse over": "Masquer le libellé des applications au passage de la souris"
"Except the hovered app": "À l'exception de l'application survolée"
"Search": "Rechercher"

View file

@ -91,3 +91,5 @@
"Applications kept in the top menu but also shown in side menu": "Applicaties blijven in het topmenu maar worden ook in het zijmenu getoond" "Applications kept in the top menu but also shown in side menu": "Applicaties blijven in het topmenu maar worden ook in het zijmenu getoond"
"These applications must be selected in the previous option.": "Deze toepassingen moeten bij de vorige optie zijn geselecteerd." "These applications must be selected in the previous option.": "Deze toepassingen moeten bij de vorige optie zijn geselecteerd."
"Hide labels on mouse over": "Hide labels on mouse over" "Hide labels on mouse over": "Hide labels on mouse over"
"Except the hovered app": "Except the hovered app"
"Search": "Search"

View file

@ -91,3 +91,5 @@
"Applications kept in the top menu but also shown in side 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": "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." "These applications must be selected in the previous option.": "These applications must be selected in the previous option."
"Hide labels on mouse over": "Скрыть название при наведении мыши" "Hide labels on mouse over": "Скрыть название при наведении мыши"
"Except the hovered app": "Except the hovered app"
"Search": "Search"

View file

@ -93,3 +93,5 @@
"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.": ""
"Hide labels on mouse over": "" "Hide labels on mouse over": ""
"Except the hovered app": ""
"Search": ""

View file

@ -91,3 +91,5 @@
"Applications kept in the top menu but also shown in side 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": "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." "These applications must be selected in the previous option.": "These applications must be selected in the previous option."
"Hide labels on mouse over": "Hide labels on mouse over" "Hide labels on mouse over": "Hide labels on mouse over"
"Except the hovered app": "Except the hovered app"
"Search": "Search"

View file

@ -42,10 +42,16 @@
top: 49px; top: 49px;
} }
#side-menu.hide-opener .side-menu-header { #side-menu.hide-opener .side-menu-header .side-menu-opener.side-menu-closer
{
visibility: hidden; visibility: hidden;
} }
#side-menu.hide-opener.side-menu-with-categories .side-menu-search
{
float: none;
}
<?php if ($_['size-text'] === 'hidden'): ?> <?php if ($_['size-text'] === 'hidden'): ?>
#side-menu, .side-menu-apps-list { #side-menu, .side-menu-apps-list {
<?php if ($_['size-icon'] === 'big'): ?> <?php if ($_['size-icon'] === 'big'): ?>

View file

@ -878,9 +878,15 @@ $labelAlwaysDisplayed = 'Always displayed';
<div class="side-menu-setting-label"> <div class="side-menu-setting-label">
<?php p($l->t('Hide labels on mouse over')); ?> <?php p($l->t('Hide labels on mouse over')); ?>
</div> </div>
<?php
$choices = array_merge(
$choicesYesNo,
['Except the hovered app' => '2']
);
?>
<div class="side-menu-setting-form"> <div class="side-menu-setting-form">
<select id="side-menu-top-menu-mouse-over-hidden-label" name="top-menu-mouse-over-hidden-label" class="side-menu-setting"> <select id="side-menu-top-menu-mouse-over-hidden-label" name="top-menu-mouse-over-hidden-label" class="side-menu-setting">
<?php foreach ($choicesYesNo as $label => $value): ?> <?php foreach ($choices as $label => $value): ?>
<option value="<?php echo $value ?>" <?php if ($value === $_['top-menu-mouse-over-hidden-label']): ?>selected<?php endif; ?>> <option value="<?php echo $value ?>" <?php if ($value === $_['top-menu-mouse-over-hidden-label']): ?>selected<?php endif; ?>>
<?php echo $l->t($label); ?> <?php echo $l->t($label); ?>
</option> </option>