From b900ed7a4d822a722cd3317ca8785f737f6676a0 Mon Sep 17 00:00:00 2001 From: Simon Vieille Date: Mon, 2 Aug 2021 21:48:58 +0200 Subject: [PATCH 001/114] release v1.26.0 --- CHANGELOG.md | 4 ++++ appinfo/info.xml | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b0fc110..bcf85bb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ ## [Unreleased] +## 1.26.0 +### Added +- add czech translation + ## 1.25.2 ### Fixed - fix CHANGELOG diff --git a/appinfo/info.xml b/appinfo/info.xml index a9c31f2..6ae1aed 100644 --- a/appinfo/info.xml +++ b/appinfo/info.xml @@ -26,7 +26,7 @@ If you like this application and if you want to support the development: * [Donate with liberapay](https://liberapay.com/deblan) * [Leave a comment](https://apps.nextcloud.com/apps/side_menu#comments) ]]> - 1.25.2 + 1.26.0 agpl Simon Vieille SideMenu From 5e4a7bcae452d17e63eb737a36f04591d2d2d347 Mon Sep 17 00:00:00 2001 From: Simon Vieille Date: Wed, 11 Aug 2021 11:57:01 +0200 Subject: [PATCH 002/114] improved German translations --- src/l10n/fixtures/de.yaml | 62 +++++++++++++++++++-------------------- 1 file changed, 31 insertions(+), 31 deletions(-) diff --git a/src/l10n/fixtures/de.yaml b/src/l10n/fixtures/de.yaml index 254e5b7..e67d0fc 100644 --- a/src/l10n/fixtures/de.yaml +++ b/src/l10n/fixtures/de.yaml @@ -1,31 +1,31 @@ "Custom menu": "Benutzerdefiniertes Menü" -"Enable the custom menu": "Aktiviere das Benutzerdefiniertes Menü" +"Enable the custom menu": "Benutzerdefiniertes Menü aktivieren" "No": "Nein" "Yes": "Ja" "Menu": "Menü" 'Use the shortcut Ctrl+o to open and to hide the side menu. Use tab to navigate.': 'Verwende die Tastenkombination Strg+o, um das Seitenmenü ein- und auszublenden. Verwende tab zum Navigieren.' -"Top menu": "Hauptmenü" -"Apps that not must be moved in the side menu": "Apps, die nicht ins Seitenmenü verschoben werden müssen" +"Top menu": "Obere Navigationsleiste" +"Apps that not must be moved in the side menu": "Anwendungen, die nicht ins Seitenmenü verschoben werden sollen" "If there is no selection then the global configuration is applied.": "Wenn keine Auswahl vorhanden ist, wird die globale Konfiguration angewendet." "Experimental": "Experimentell" "Save": "Speichern" -"You like this app and you want to support me?": "Du magst diese App und möchtest mich unterstützen?" -"Buy me a coffee ☕": "Gib mir einen Kaffee ☕" -"Hidden": "Versteckt" +"You like this app and you want to support me?": "Du magst diese Anwendung und möchtest mich unterstützen?" +"Buy me a coffee ☕": "Gib mir einen Kaffee aus ☕" +"Hidden": "Ausblenden" "Small": "Klein" "Normal": "Normal" "Big": "Groß" "Colors": "Farben" "Background color": "Hintergrundfarbe" -"Background color of current app": "Hintergrundfarbe der aktuellen App" +"Background color of current app": "Hintergrundfarbe der aktuellen Anwendung" "Text color": "Textfarbe" "Loader": "Ladestandanzeige" "Icon": "Symbol" "Same color": "Selbe Farbe" "Opposite color": "Gegenfarbe" "Transparent": "Transparent" -"Opaque": "Undurchsichtig" -"Opener": "Öffner" +"Opaque": "Nicht transparent" +"Opener": "Menü-Symbol" "Default": "Standard" "Default (dark)": "Standard (dunkel)" "Hamburger": "Hamburger" @@ -35,44 +35,44 @@ "Before the logo": "Vor dem Logo" "After the logo": "Nach dem Logo" "Position": "Position" -"Show only the opener (hidden logo)": "Nur den Öffner anzeigen (verstecktes Logo)" -"Do not display the side menu and the opener if there is no application (eg: public pages).": "Zeige das Seitenmenü und den Öffner nicht an, wenn keine Anwendung vorhanden ist (z.B. bei öffentlichen Seiten)." +"Show only the opener (hidden logo)": "Nur das Menü-Symbol anzeigen (Logo wird ausgeblendet)" +"Do not display the side menu and the opener if there is no application (eg: public pages).": "Zeige das Seitenmenü und das Menü-Symbol nicht an, wenn keine Anwendung vorhanden ist (z.B. bei öffentlichen Seiten)." "Panel": "Panel" -"Open the menu when the mouse is hover the opener (automatically disabled on touch screens)": "Öffne das Menü, wenn die Maus über den Öffner bewegt wird (auf Touchscreens automatisch deaktiviert)." +"Open the menu when the mouse is hover the opener (automatically disabled on touch screens)": "Öffne das Menü, wenn die Maus über das Menü-Symbol bewegt wird (auf Touchscreens automatisch deaktiviert)." "Display the big menu": "Großes Menü anzeigen" -"The big menu is not compatible with AppOrder.": "Das große Menü ist nicht mit AppOrder kompatibel." +"The big menu is not compatible with AppOrder.": "Das große Menü ist nicht mit AppOrder kompatibel." "Display the logo": "Logo anzeigen" -"This feature is not compatible with the big menu display.": "Diese Funktion ist nicht mit großes Menü kompatibel." +"This feature is not compatible with the big menu display.": "Diese Funktion ist nicht mit dem großen Menü kompatibel." "Icons and texts": "Symbole und Texte" -"Loader enabled": "Loader aktiviert" +"Loader enabled": "Ladestandanzeige aktiviert" "Tips": "Tipps" -"Always displayed": "Wird immer angezeigt" +"Always displayed": "Immer anzeigen" "The logo will be hidden when the menu is always displayed.": "Das Logo wird ausgeblendet, wenn das Menü immer angezeigt wird." "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." "Big menu": "Großes Menü" -"Live preview": "Live Vorschau" -"Open apps in new tab": "Öffne Apps in einem neuen Tab" +"Live preview": "Live-Vorschau" +"Open apps in new tab": "Öffne Anwendungen in einem neuen Tab" "Use the global setting": "Verwende die globale Einstellung" "Use my selection": "Verwende meine Auswahl" "Show and hide the list of applications": "Ein- und Ausblenden der Anwendungsliste" -"Use the avatar instead of the logo": "Verwenden Sie den Avatar anstelle des Logos" -"You do not have permission to change the settings.": "Sie haben keine Berechtigung zum Ändern der Einstellungen." -"Force this configuration to users": "Erzwingen Sie diese Konfiguration für Benutzer" -"Export the configuration": "Exportieren Sie die Konfiguration" -"Purge the cache": "Leeren Sie den Cache" -"Show the link to settings": "Zeigen Sie den Link zu den Einstellungen an" -"The menu is enabled by default for users": "Das Menü ist standardmäßig für Benutzer aktiviert" -"Except when the configuration is forced.": "Außer wenn die Konfiguration erzwungen wird." -"Apps that should not be displayed in the menu": "Apps, die nicht im Menü angezeigt werden sollen" -"This feature is only compatible with the big menu display.": "Kompatibel mit der Anzeige Großes Menü ." +"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, die Einstellungen dieser Anwendung zu ändern." +"Force this configuration to users": "Konfiguration für alle Benutzer erzwingen" +"Export the configuration": "Konfiguration exportieren" +"Purge the cache": "Cache leeren" +"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 aktiviert" +"Except when the configuration is forced.": "Gilt nicht, wenn die Konfiguration erzwungen wird." +"Apps that should not be displayed in the menu": "Anwendungen, die nicht im Menü angezeigt werden sollen" +"This feature is only compatible with the big menu display.": "Kompatibel mit dem großen Menü." "The logo is a link to the default app": "Das Logo ist ein Link zur Standard-App" "Others": "Andere" "Categories": "Kategorien" "Customize sorting": "Sortierung anpassen" "Order by": "Sortieren nach" "Name": "Name" -"Customed": "Kundenspezifisch" +"Customed": "Benutzerdefiniert" "Show and hide the list of categories": "Liste der Kategorien ein- und ausblenden" -"This parameters are used when Dark theme or Breeze Dark Theme are enabled.": "Diese Parameter werden verwendet, wenn Dark Theme oder Breeze Dark Theme aktiviert sind." -"Dark mode colors": "Dunkle Modusfarben" +"This parameters are used when Dark theme or Breeze Dark Theme are enabled.": "Diese Optionen werden auf Dark Theme oder Breeze Dark Theme angewendet." +"Dark mode colors": "Farben für den dunklen Modus" From a72bb87aa265206787159231368171b9e12dd6b8 Mon Sep 17 00:00:00 2001 From: Simon Vieille Date: Wed, 11 Aug 2021 11:57:43 +0200 Subject: [PATCH 003/114] hide personal settings access when settings are forced by the administrator --- lib/Settings/PersonalSection.php | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/lib/Settings/PersonalSection.php b/lib/Settings/PersonalSection.php index 101d95f..58a773f 100644 --- a/lib/Settings/PersonalSection.php +++ b/lib/Settings/PersonalSection.php @@ -19,6 +19,7 @@ namespace OCA\SideMenu\Settings; use OCA\SideMenu\AppInfo\Application; +use OCA\SideMenu\Service\ConfigProxy; use OCP\IL10N; use OCP\IURLGenerator; use OCP\Settings\IIconSection; @@ -35,10 +36,16 @@ class PersonalSection implements IIconSection */ private $url; - public function __construct(IURLGenerator $url, IL10N $l) + /** + * @var ConfigProxy + */ + private $configProxy; + + public function __construct(IURLGenerator $url, IL10N $l, ConfigProxy $configProxy) { $this->url = $url; $this->l = $l; + $this->configProxy = $configProxy; } /** @@ -49,6 +56,10 @@ class PersonalSection implements IIconSection */ public function getID() { + if ($this->configProxy->getAppValueBool('force', '0')) { + return ''; + } + return Application::APP_ID; } @@ -60,6 +71,10 @@ class PersonalSection implements IIconSection */ public function getName() { + if ($this->configProxy->getAppValueBool('force', '0')) { + return ''; + } + return $this->l->t(Application::APP_NAME); } @@ -72,6 +87,10 @@ class PersonalSection implements IIconSection */ public function getPriority() { + if ($this->configProxy->getAppValueBool('force', '0')) { + return null; + } + return 70; } From 33d7c6a7f04788c8fd35174c7504bb080f67591b Mon Sep 17 00:00:00 2001 From: Simon Vieille Date: Wed, 11 Aug 2021 11:58:15 +0200 Subject: [PATCH 004/114] release v1.27.0 --- CHANGELOG.md | 8 +++++++- appinfo/info.xml | 2 +- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bcf85bb..e7f6605 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,8 +1,14 @@ ## [Unreleased] +## 1.27.0 +### Added +- hide personal settings access when settings are forced by the administrator +### Fixed +- improve German translations + ## 1.26.0 ### Added -- add czech translation +- add Czech translation ## 1.25.2 ### Fixed diff --git a/appinfo/info.xml b/appinfo/info.xml index 6ae1aed..7ce5afb 100644 --- a/appinfo/info.xml +++ b/appinfo/info.xml @@ -26,7 +26,7 @@ If you like this application and if you want to support the development: * [Donate with liberapay](https://liberapay.com/deblan) * [Leave a comment](https://apps.nextcloud.com/apps/side_menu#comments) ]]> - 1.26.0 + 1.27.0 agpl Simon Vieille SideMenu From db0fa60ce8c2accaa079b012e78e072464e1ef7d Mon Sep 17 00:00:00 2001 From: Simon Vieille Date: Wed, 11 Aug 2021 14:43:04 +0200 Subject: [PATCH 005/114] fix German translation render --- templates/settings/admin-form.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/templates/settings/admin-form.php b/templates/settings/admin-form.php index 82196eb..0a21220 100644 --- a/templates/settings/admin-form.php +++ b/templates/settings/admin-form.php @@ -217,7 +217,7 @@ $choicesSizes = [

- t('This parameters are used when Dark theme or Breeze Dark Theme are enabled.')); ?> + t('This parameters are used when Dark theme or Breeze Dark Theme are enabled.'); ?>

@@ -469,7 +469,7 @@ $choicesSizes = [
-

t('The big menu is not compatible with AppOrder.')); ?>

+

t('The big menu is not compatible with AppOrder.'); ?>

Date: Wed, 11 Aug 2021 14:43:52 +0200 Subject: [PATCH 006/114] release v1.27.1 --- CHANGELOG.md | 4 ++++ appinfo/info.xml | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e7f6605..5d7e94e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ ## [Unreleased] +## 1.27.1 +### Fixed +- fix German translation render + ## 1.27.0 ### Added - hide personal settings access when settings are forced by the administrator diff --git a/appinfo/info.xml b/appinfo/info.xml index 7ce5afb..cfc9ff9 100644 --- a/appinfo/info.xml +++ b/appinfo/info.xml @@ -26,7 +26,7 @@ If you like this application and if you want to support the development: * [Donate with liberapay](https://liberapay.com/deblan) * [Leave a comment](https://apps.nextcloud.com/apps/side_menu#comments) ]]> - 1.27.0 + 1.27.1 agpl Simon Vieille SideMenu From 1896bb399dc71f97031396a1c445e1b0a81dd561 Mon Sep 17 00:00:00 2001 From: Simon Vieille Date: Thu, 21 Oct 2021 20:06:17 +0200 Subject: [PATCH 007/114] fix #62: hide app notification icon --- CHANGELOG.md | 4 ++++ appinfo/info.xml | 2 +- css/sideMenu.css | 4 ++++ 3 files changed, 9 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5d7e94e..0c50218 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ ## [Unreleased] +## 1.27.2 +### Fixed +- fix #62: hide app notification icon + ## 1.27.1 ### Fixed - fix German translation render diff --git a/appinfo/info.xml b/appinfo/info.xml index cfc9ff9..392e3d6 100644 --- a/appinfo/info.xml +++ b/appinfo/info.xml @@ -26,7 +26,7 @@ If you like this application and if you want to support the development: * [Donate with liberapay](https://liberapay.com/deblan) * [Leave a comment](https://apps.nextcloud.com/apps/side_menu#comments) ]]> - 1.27.1 + 1.27.2 agpl Simon Vieille SideMenu diff --git a/css/sideMenu.css b/css/sideMenu.css index 466c678..e78ffa0 100644 --- a/css/sideMenu.css +++ b/css/sideMenu.css @@ -105,6 +105,10 @@ margin-top: -3px; } +.side-menu-app-icon .app-icon-notification { + display: none; +} + .side-menu-app a { line-height: 30px; color: var(--side-menu-text-color, #fff); From 3975f3be2881cda8b014d71106c3dd260b8b9d06 Mon Sep 17 00:00:00 2001 From: Simon Vieille Date: Thu, 21 Oct 2021 21:37:32 +0200 Subject: [PATCH 008/114] fix #63: add a new side menu with categories --- css/sideMenu.css | 24 ++- img/admin/layout-side-with-categories.svg | 223 ++++++++++++++++++++++ lib/Controller/JsController.php | 1 + lib/Settings/Admin.php | 1 + src/SideMenu.js | 6 +- src/SideMenuWithCategories.vue | 126 ++++++++++++ src/admin.js | 1 + src/l10n/fixtures/cs.yaml | 3 +- src/l10n/fixtures/de.yaml | 3 +- src/l10n/fixtures/fr.yaml | 3 +- src/l10n/fixtures/zh_CN.yaml | 3 +- templates/js/script.php | 28 ++- templates/settings/admin-form.php | 66 +++++-- 13 files changed, 453 insertions(+), 35 deletions(-) create mode 100644 img/admin/layout-side-with-categories.svg create mode 100644 src/SideMenuWithCategories.vue diff --git a/css/sideMenu.css b/css/sideMenu.css index e78ffa0..08b0539 100644 --- a/css/sideMenu.css +++ b/css/sideMenu.css @@ -162,23 +162,23 @@ transition-property: width; } -#side-menu.side-menu-big { +#side-menu.side-menu-big, #side-menu.side-menu-with-categories { max-width: 100%; height: auto; } -.side-menu-big .side-menu-header { +.side-menu-big .side-menu-header, .side-menu-with-categories .side-menu-header { height: auto; } -.side-menu-big .side-menu-apps-list { +.side-menu-big .side-menu-apps-list, .side-menu-with-categories .side-menu-apps-list { height: auto; position: static; max-width: 100vw; overflow: auto; } -.side-menu-big .side-menu-app a { +.side-menu-big .side-menu-app a, .side-menu-with-categories .side-menu-app a { padding: 7px 0 7px 7px; } @@ -217,7 +217,7 @@ stroke: var(--side-menu-text-color, #fff); } -.side-menu-big .side-menu-app-icon { +.side-menu-with-categories .side-menu-app-icon, .side-menu-big .side-menu-app-icon { vertical-align: middle; margin-top: -2px; } @@ -271,6 +271,20 @@ transform: translateX(calc(-100% + 50px)) !important; } +#side-menu.side-menu-with-categories { + max-width: 290px; + height: 100vh; +} + +.side-menu-with-categories .side-menu-categories { + display: block; + padding: 0; +} + +.side-menu-with-categories .side-menu-category { + padding: 10px 0; +} + @media screen and (max-width: 1024px) { #side-menu.side-menu-big { max-width: 290px; diff --git a/img/admin/layout-side-with-categories.svg b/img/admin/layout-side-with-categories.svg new file mode 100644 index 0000000..739d5b2 --- /dev/null +++ b/img/admin/layout-side-with-categories.svg @@ -0,0 +1,223 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/lib/Controller/JsController.php b/lib/Controller/JsController.php index b5ed845..135b1ab 100644 --- a/lib/Controller/JsController.php +++ b/lib/Controller/JsController.php @@ -152,6 +152,7 @@ class JsController extends Controller 'hide-when-no-apps' => $this->config->getAppValueBool('hide-when-no-apps', '0'), 'loader-enabled' => $this->config->getAppValueBool('loader-enabled', '1'), 'always-displayed' => $this->config->getAppValueBool('always-displayed', '0'), + 'side-with-categories' => $this->config->getAppValueBool('side-with-categories', '0'), 'big-menu' => $this->config->getAppValueBool('big-menu', '0'), 'big-menu-hidden-apps' => $this->config->getAppValueArray('big-menu-hidden-apps', '[]'), 'avatar' => $avatar, diff --git a/lib/Settings/Admin.php b/lib/Settings/Admin.php index d77978c..8885c13 100644 --- a/lib/Settings/Admin.php +++ b/lib/Settings/Admin.php @@ -102,6 +102,7 @@ class Admin implements ISettings 'cache' => $this->config->getAppValue('cache', '0'), 'always-displayed' => $this->config->getAppValue('always-displayed', '0'), 'big-menu' => $this->config->getAppValue('big-menu', '0'), + 'side-with-categories' => $this->config->getAppValue('side-with-categories', '0'), 'big-menu-hidden-apps' => $this->config->getAppValueArray('big-menu-hidden-apps', '[]'), 'display-logo' => $this->config->getAppValue('display-logo', '1'), 'add-logo-link' => $this->config->getAppValue('add-logo-link', '1'), diff --git a/src/SideMenu.js b/src/SideMenu.js index 05d5e17..441d0a4 100644 --- a/src/SideMenu.js +++ b/src/SideMenu.js @@ -18,11 +18,9 @@ import Vue from 'vue' import SideMenu from './SideMenu.vue' import SideMenuBig from './SideMenuBig.vue' +import SideMenuWithCategories from './SideMenuWithCategories.vue' -// Vue.prototype.t = t Vue.prototype.OC = OC -// Vue.prototype.OC = OCP - const mountSideMenuComponent = () => { const sideMenuContainer = document.querySelector('#side-menu') @@ -32,6 +30,8 @@ const mountSideMenuComponent = () => { if (sideMenuContainer.getAttribute('data-bigmenu')) { component = SideMenuBig + } else if(sideMenuContainer.getAttribute('data-sidewithcategories')) { + component = SideMenuWithCategories } else { component = SideMenu } diff --git a/src/SideMenuWithCategories.vue b/src/SideMenuWithCategories.vue new file mode 100644 index 0000000..6b63689 --- /dev/null +++ b/src/SideMenuWithCategories.vue @@ -0,0 +1,126 @@ + + + + diff --git a/src/admin.js b/src/admin.js index 2a0a668..c9b3c4d 100644 --- a/src/admin.js +++ b/src/admin.js @@ -103,6 +103,7 @@ jQuery(document).ready(() => { jQuery('#side-menu-always-displayed').val(target.attr('data-alwaysdiplayed')) jQuery('#side-menu-big-menu').val(target.attr('data-bigmenu')) + jQuery('#side-menu-side-with-categories').val(target.attr('data-sidewithcategories')) }) jQuery('.side-menu-setting-live').on('change', (event) => { diff --git a/src/l10n/fixtures/cs.yaml b/src/l10n/fixtures/cs.yaml index a16b4ca..215c0e3 100644 --- a/src/l10n/fixtures/cs.yaml +++ b/src/l10n/fixtures/cs.yaml @@ -40,7 +40,7 @@ "Panel": "Panel" "Open the menu when the mouse is hover the opener (automatically disabled on touch screens)": "Otevřít nabídku při najetím ukazatelem na tlačítko nabídky (automaticky vypnuto pro dotykové obrazovky)." "Display the big menu": "Zobrazit velkou nabídku" -"The big menu is not compatible with AppOrder.": "Velká nabídka není kompatibilní s jinou aplikací (doplňkem) „Pořadí aplikací“." +"This menu is not compatible with AppOrder.": "Nabídka není kompatibilní s jinou aplikací (doplňkem) „Pořadí aplikací“." "Display the logo": "Zobrazit logo" "This feature is not compatible with the big menu display.": "Tato funkce není kompatibilní se zobrazením velké nabídky." "Icons and texts": "Ikony a texty" @@ -76,3 +76,4 @@ "Show and hide the list of categories": "Zobrazit/skrýt seznam kategorií" "This parameters are used when Dark theme or Breeze Dark Theme are enabled.": "Tyto parametry jsou použity v případě, že je zapnutý (Breeze) tmavý motiv vzhledu." "Dark mode colors": "Barvy tmavého režimu" +"With categories": "S kategoriemi" diff --git a/src/l10n/fixtures/de.yaml b/src/l10n/fixtures/de.yaml index e67d0fc..5d8353a 100644 --- a/src/l10n/fixtures/de.yaml +++ b/src/l10n/fixtures/de.yaml @@ -40,7 +40,7 @@ "Panel": "Panel" "Open the menu when the mouse is hover the opener (automatically disabled on touch screens)": "Öffne das Menü, wenn die Maus über das Menü-Symbol bewegt wird (auf Touchscreens automatisch deaktiviert)." "Display the big menu": "Großes Menü anzeigen" -"The big menu is not compatible with AppOrder.": "Das große Menü ist nicht mit AppOrder kompatibel." +"This menu is not compatible with AppOrder.": "Dieses Menü ist nicht mit AppOrder kompatibel." "Display the logo": "Logo anzeigen" "This feature is not compatible with the big menu display.": "Diese Funktion ist nicht mit dem großen Menü kompatibel." "Icons and texts": "Symbole und Texte" @@ -76,3 +76,4 @@ "Show and hide the list of categories": "Liste der Kategorien ein- und ausblenden" "This parameters are used when Dark theme or Breeze Dark Theme are enabled.": "Diese Optionen werden auf Dark Theme oder Breeze Dark Theme angewendet." "Dark mode colors": "Farben für den dunklen Modus" +"With categories": "Mit Kategorien" diff --git a/src/l10n/fixtures/fr.yaml b/src/l10n/fixtures/fr.yaml index 778a0cf..56b286e 100644 --- a/src/l10n/fixtures/fr.yaml +++ b/src/l10n/fixtures/fr.yaml @@ -40,7 +40,7 @@ "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)" "Display the big menu": "Afficher le menu large" -"The big menu is not compatible with AppOrder.": "Le menu large n'est pas compatible avec l'application AppOrder" +"This menu is not compatible with AppOrder.": "Ce menu n'est pas compatible avec l'application AppOrder" "Display the logo": "Afficher le logo" "This feature is not compatible with the big menu display.": "Cette fonctionnalité n'est pas compatible avec l'affichage du menu large." "Icons and texts": "Icônes et textes" @@ -76,3 +76,4 @@ "Show and hide the list of categories": "Afficher et masquer la liste des catégories" "This parameters are used when Dark theme or Breeze Dark Theme are enabled.": "Ces paramètres sont utilisés lorsque le thème sombre ou le thème Breeze Dark sont activés." "Dark mode colors": "Couleurs du mode sombre" +"With categories": "Avec les catégories" diff --git a/src/l10n/fixtures/zh_CN.yaml b/src/l10n/fixtures/zh_CN.yaml index 9a43ff0..969583d 100644 --- a/src/l10n/fixtures/zh_CN.yaml +++ b/src/l10n/fixtures/zh_CN.yaml @@ -40,7 +40,7 @@ "Panel": "面板" "Open the menu when the mouse is hover the opener (automatically disabled on touch screens)": "鼠标悬停时打开菜单 (触摸屏时将自动禁用)" "Display the big menu": "显示大型菜单" -"The big menu is not compatible with AppOrder.": "大型菜单与应用顺序不兼容" +"This menu is not compatible with AppOrder.": "型菜单与应用顺序不兼容" "Display the logo": "显示logo" "This feature is not compatible with the big menu<\/code> display.": "此功能与显示大型菜单<\/code>不兼容。" "Icons and texts": "图标与文字" @@ -76,3 +76,4 @@ "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": "有类别" diff --git a/templates/js/script.php b/templates/js/script.php index 3f834fe..cf4b2ce 100644 --- a/templates/js/script.php +++ b/templates/js/script.php @@ -1,3 +1,17 @@ + + (function() { var sideMenuContainer = jQuery('

') var sideMenuOpener = jQuery('') @@ -6,8 +20,10 @@ var html = jQuery('html') var isTouchDevice = window.matchMedia("(pointer: coarse)").matches - + sideMenu.attr('data-bigmenu', '1') + + sideMenu.attr('data-sidewithcategories', '1') var targetBlankApps = ; @@ -25,7 +41,7 @@ sideMenuOpener.removeClass('hide') } - + if (apps.length === 0) { html.removeClass('side-menu-always-displayed'); } else { @@ -33,7 +49,7 @@ } - + if (apps.length === 0) { html.removeClass('side-menu-always-displayed'); } else { @@ -61,7 +77,7 @@ } } - + var sideMenuMouseLeave = function() { sideMenu .removeClass('open') @@ -97,7 +113,7 @@ sideMenu.find('.side-menu-app.active a').focus() }) - + sideMenuOpener.on('click', function() { sideMenu.toggleClass('open') }) @@ -138,7 +154,7 @@ - + })(); diff --git a/templates/settings/admin-form.php b/templates/settings/admin-form.php index 0a21220..0f2e509 100644 --- a/templates/settings/admin-form.php +++ b/templates/settings/admin-form.php @@ -441,41 +441,56 @@ $choicesSizes = [ t('Panel')); ?> + !$_['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'], + ]; + ?> +
- - !$_['always-displayed'] && !$_['big-menu'], - 'always-displayed' => $_['always-displayed'] && !$_['big-menu'], - 'big-menu' => $_['big-menu'], - ]; - ?> -

<?php p($l->t('Default')); ?>

+
+ +
+

t('This menu is not compatible with AppOrder.'); ?>

+

+ <?php p($l->t('With categories')); ?> +

+
- -

t('The big menu is not compatible with AppOrder.'); ?>

- +

t('This menu is not compatible with AppOrder.'); ?>

<?php p($l->t('Big menu')); ?>

@@ -485,20 +500,37 @@ $choicesSizes = [ t('Experimental')); ?>
-

t('Not compatible with touch screens.')); ?>

-

<?php p($l->t('Always displayed')); ?>

- - - + + +
From 64c4d7a1e1692d10b7e07ebf7a6c3e4b3827d85d Mon Sep 17 00:00:00 2001 From: Simon Vieille Date: Thu, 21 Oct 2021 21:43:30 +0200 Subject: [PATCH 009/114] release v1.28.0 --- CHANGELOG.md | 4 ++++ appinfo/info.xml | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0c50218..ee70394 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ ## [Unreleased] +## 1.28.0 +### Added +- fix #63: add a new side menu with categories + ## 1.27.2 ### Fixed - fix #62: hide app notification icon diff --git a/appinfo/info.xml b/appinfo/info.xml index 392e3d6..97e6518 100644 --- a/appinfo/info.xml +++ b/appinfo/info.xml @@ -26,7 +26,7 @@ If you like this application and if you want to support the development: * [Donate with liberapay](https://liberapay.com/deblan) * [Leave a comment](https://apps.nextcloud.com/apps/side_menu#comments) ]]> - 1.27.2 + 1.28.0 agpl Simon Vieille SideMenu From 3b6b8ad54aab4bf630fc9c7cef10419a4ca5e4af Mon Sep 17 00:00:00 2001 From: Simon Vieille Date: Thu, 28 Oct 2021 13:16:55 +0200 Subject: [PATCH 010/114] add missing svg --- .../layout-side-menu-with-categories.svg | 224 ++++++++++++++++++ 1 file changed, 224 insertions(+) create mode 100644 img/admin/layout-side-menu-with-categories.svg diff --git a/img/admin/layout-side-menu-with-categories.svg b/img/admin/layout-side-menu-with-categories.svg new file mode 100644 index 0000000..4c8c76d --- /dev/null +++ b/img/admin/layout-side-menu-with-categories.svg @@ -0,0 +1,224 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From f26adb6ac182439866ef1349c968079ffb0ae340 Mon Sep 17 00:00:00 2001 From: Simon Vieille Date: Thu, 28 Oct 2021 13:50:16 +0200 Subject: [PATCH 011/114] remove jquery from components --- src/SideMenu.vue | 4 +++- src/SideMenuBig.vue | 4 +++- src/SideMenuWithCategories.vue | 4 +++- templates/js/script.php | 4 +++- 4 files changed, 12 insertions(+), 4 deletions(-) diff --git a/src/SideMenu.vue b/src/SideMenu.vue index 68fcccb..908f84c 100644 --- a/src/SideMenu.vue +++ b/src/SideMenu.vue @@ -128,7 +128,9 @@ export default { (function(apps) { window.setTimeout(function() { - jQuery('body').trigger('side-menu.apps', [apps]) + document.querySelector('body').dispatchEvent(new CustomEvent('side-menu.apps', { + detail: {apps: apps}, + })); }, 1000) })(this.apps) }, diff --git a/src/SideMenuBig.vue b/src/SideMenuBig.vue index 9f6d8f2..31b9992 100644 --- a/src/SideMenuBig.vue +++ b/src/SideMenuBig.vue @@ -97,7 +97,9 @@ export default { } } - jQuery('body').trigger('side-menu.apps', [apps]) + document.querySelector('body').dispatchEvent(new CustomEvent('side-menu.apps', { + detail: {apps: apps}, + })); }); }, diff --git a/src/SideMenuWithCategories.vue b/src/SideMenuWithCategories.vue index 6b63689..0ba9c45 100644 --- a/src/SideMenuWithCategories.vue +++ b/src/SideMenuWithCategories.vue @@ -95,7 +95,9 @@ export default { } } - jQuery('body').trigger('side-menu.apps', [apps]) + document.querySelector('body').dispatchEvent(new CustomEvent('side-menu.apps', { + detail: {apps: apps}, + })); }); }, diff --git a/templates/js/script.php b/templates/js/script.php index cf4b2ce..1bf944b 100644 --- a/templates/js/script.php +++ b/templates/js/script.php @@ -28,7 +28,9 @@ if ($_['always-displayed']) { var targetBlankApps = ; - body.on('side-menu.apps', function(e, apps) { + document.querySelector('body').addEventListener('side-menu.apps', function(e) { + const apps = e.detail.apps; + sideMenu = jQuery('#side-menu') From cc7c9f6ed578dc9bc63c84c75f6278b48d043c39 Mon Sep 17 00:00:00 2001 From: Simon Vieille Date: Thu, 28 Oct 2021 15:47:03 +0200 Subject: [PATCH 012/114] remove most of jquery --- src/SideMenu.js | 2 +- templates/js/_alwaysDisplayed.js | 20 ++++- templates/js/_loaderEnabled.js | 17 ++-- templates/js/script.php | 146 +++++++++++++++++++------------ 4 files changed, 115 insertions(+), 70 deletions(-) diff --git a/src/SideMenu.js b/src/SideMenu.js index 441d0a4..a9848c4 100644 --- a/src/SideMenu.js +++ b/src/SideMenu.js @@ -41,7 +41,7 @@ const mountSideMenuComponent = () => { sideMenu.$mount('#side-menu') - $('body').trigger('side-menu.ready') + document.querySelector('body').dispatchEvent(new CustomEvent('side-menu.ready')) } else { window.setTimeout(mountSideMenuComponent, 50) } diff --git a/templates/js/_alwaysDisplayed.js b/templates/js/_alwaysDisplayed.js index 2b58bc7..f56b2a2 100644 --- a/templates/js/_alwaysDisplayed.js +++ b/templates/js/_alwaysDisplayed.js @@ -1,5 +1,5 @@ var alwaysDisplayed = function() { - var elements = document.querySelectorAll('*'); + var elements = querySelectorAll('*') var fixedElements = [] for (var i in elements) { @@ -25,7 +25,21 @@ var alwaysDisplayed = function() { continue } - if (jQuery(element).parents('#side-menu').length) { + let elementIsInSideMenu = false + let parent = element.parentNode + + while (parent && !elementIsInSideMenu) { + try { + if (parent.getAttribute('id') === 'side-menu') { + elementIsInSideMenu = true + } + } catch (e) { + } + + parent = parent.parentNode + } + + if (elementIsInSideMenu) { continue } @@ -56,7 +70,7 @@ if (content && content.classList.contains('app-settings')) { } const observer = new MutationObserver(() => { if (loaded) { - return; + return } const element = content.querySelector('#app-category-your-apps') || content.querySelector('#app-navigation ul') diff --git a/templates/js/_loaderEnabled.js b/templates/js/_loaderEnabled.js index b391e4b..1d5ff4e 100644 --- a/templates/js/_loaderEnabled.js +++ b/templates/js/_loaderEnabled.js @@ -1,15 +1,14 @@ -var pageLoader = jQuery('
') -var pageLoaderBar = jQuery('
') +let pageLoader = createElement('div', {id: 'side-menu-loader'}) +let pageLoaderBar = createElement('div', {id: 'side-menu-loader-bar'}) -body.append(pageLoader) -pageLoader.append(pageLoaderBar) +pageLoader.appendChild(pageLoaderBar) +querySelector('body').appendChild(pageLoader) -var pageLoaderValue = 0 - -$(window).on('beforeunload', function() { - setInterval(function() { - pageLoaderBar.width(pageLoaderValue.toString() + '%') +let pageLoaderValue = 0 +window.addEventListener('beforeunload', () => { + setInterval(() => { + pageLoaderBar.style.width = pageLoaderValue.toString() + '%' pageLoaderValue = Math.min(pageLoaderValue + .2, 100) }, 25) }) diff --git a/templates/js/script.php b/templates/js/script.php index 1bf944b..e32af22 100644 --- a/templates/js/script.php +++ b/templates/js/script.php @@ -13,65 +13,94 @@ if ($_['always-displayed']) { ?> (function() { - var sideMenuContainer = jQuery('
') - var sideMenuOpener = jQuery('') - var sideMenu = jQuery('
') - var body = jQuery('body') - var html = jQuery('html') - var isTouchDevice = window.matchMedia("(pointer: coarse)").matches + const querySelector = function(selector, element) { + if (element) { + return element.querySelector(selector) + } + + return document.querySelector(selector) + } + + const querySelectorAll = function(selector, element) { + if (element) { + return element.querySelectorAll(selector) + } + + return document.querySelectorAll(selector) + } + + const createElement = function(tagName, attributes) { + const element = document.createElement(tagName) + + if (typeof attributes === 'object') { + for (let i in attributes) { + element.setAttribute(i, attributes[i]) + } + } + + return element + } + + const sideMenuContainer = createElement('div', {id: 'side-menu-container'}) + const sideMenuOpener = createElement('button', {'class': 'side-menu-opener'}) + const sideMenu = createElement('div', {id: 'side-menu'}) + + const body = querySelector('body') + const html = querySelector('html') + const nextcloud = querySelector('#nextcloud') + + const isTouchDevice = window.matchMedia("(pointer: coarse)").matches + const targetBlankApps = - sideMenu.attr('data-bigmenu', '1') + sideMenu.setAttribute('data-bigmenu', '1') - sideMenu.attr('data-sidewithcategories', '1') + sideMenu.setAttribute('data-sidewithcategories', '1') - var targetBlankApps = ; - - document.querySelector('body').addEventListener('side-menu.apps', function(e) { + querySelector('body').addEventListener('side-menu.apps', (e) => { const apps = e.detail.apps; - sideMenu = jQuery('#side-menu') + const sideMenu = querySelector('#side-menu') if (apps.length === 0) { - sideMenu.removeClass('open') - sideMenu.addClass('hide') - sideMenuOpener.addClass('hide') + sideMenu.classList.remove('open') + sideMenu.classList.add('hide') + sideMenuOpener.classList.add('hide') } else { - sideMenu.removeClass('hide') - sideMenuOpener.removeClass('hide') + sideMenu.classList.remove('hide') + sideMenuOpener.classList.remove('hide') } if (apps.length === 0) { - html.removeClass('side-menu-always-displayed'); + html.classList.remove('side-menu-always-displayed') } else { - html.addClass('side-menu-always-displayed'); + html.classList.add('side-menu-always-displayed') } if (apps.length === 0) { - html.removeClass('side-menu-always-displayed'); + html.classList.remove('side-menu-always-displayed') } else { - html.addClass('side-menu-always-displayed'); + html.classList.add('side-menu-always-displayed') } }) - body.on('side-menu.ready', function() { - sideMenu = jQuery('#side-menu') + body.addEventListener('side-menu.ready', () => { + const sideMenu = querySelector('#side-menu') + const headerMenuOpener = querySelector('#header .side-menu-opener') + const sideMenuOpener = querySelector('#side-menu .side-menu-opener') - var headerMenuOpener = jQuery('#header .side-menu-opener') - var sideMenuOpener = jQuery('#side-menu .side-menu-opener') - - sideMenuFocus = function() { - var a = sideMenu.find('.side-menu-app.active a') + sideMenuFocus = () => { + let a = querySelector('.side-menu-app.active a', sideMenu) if (a.length === 0) { - a = sideMenu.find('.side-menu-app:first-child a') + a = querySelector('.side-menu-app:first-child a', sideMenu) } if (a.length > 0) { @@ -80,78 +109,81 @@ if ($_['always-displayed']) { } - var sideMenuMouseLeave = function() { - sideMenu - .removeClass('open') - .off('mouseleave', sideMenuMouseLeave) + const sideMenuMouseLeave = () => { + sideMenu.classList.remove('open') + sideMenu.removeEventListener('mouseleave', sideMenuMouseLeave) } - var sideMenuMouseEnter = function() { - sideMenu.on('mouseleave', sideMenuMouseLeave) + const sideMenuMouseEnter = () => { + sideMenu.addEventListener('mouseleave', sideMenuMouseLeave) } - var sideMenuOpenerMouseEnter = function() { - sideMenu - .addClass('open') - .on('mouseenter', sideMenuMouseEnter) + const sideMenuOpenerMouseEnter = () => { + sideMenu.classList.add('open') + sideMenu.addEventListener('mouseenter', sideMenuMouseEnter) sideMenuFocus() } if (!isTouchDevice) { - headerMenuOpener.on('mouseenter', sideMenuOpenerMouseEnter) + headerMenuOpener.addEventListener('mouseenter', sideMenuOpenerMouseEnter) - sideMenu.addClass('hide-opener') + sideMenu.classList.add('hide-opener') - sideMenu.on('mouseleave', sideMenuMouseLeave) - sideMenu.on('mouseenter', sideMenuOpenerMouseEnter) + sideMenu.addEventListener('mouseleave', sideMenuMouseLeave) + sideMenu.addEventListener('mouseenter', sideMenuOpenerMouseEnter) } - headerMenuOpener.on('click', function() { - sideMenu.addClass('open') - sideMenu.find('.side-menu-app.active a').focus() + headerMenuOpener.addEventListener('click', () => { + sideMenu.classList.add('open') + + const a = querySelector('.side-menu-app.active a', sideMenu) + + if (a.length) { + a.focus() + } }) - sideMenuOpener.on('click', function() { - sideMenu.toggleClass('open') + sideMenuOpener.addEventListener('click', () => { + sideMenu.classList.toggle('open') }) - sideMenuOpener.on('click', function() { - sideMenu.removeClass('open') + sideMenuOpener.addEventListener('click', () => { + sideMenu.classList.remove('open') }) - jQuery(document).keydown(function(e) { + document.addEventListener('keydown', (e) => { var key = e.key || e.keyCode if ((key === 'o' || key === 79) && e.ctrlKey === true) { e.preventDefault() - sideMenu.toggleClass('open') + sideMenu.classList.toggle('open') sideMenuFocus() } }) }) - body.append(sideMenuContainer) - sideMenuContainer.append(sideMenu) + body.appendChild(sideMenuContainer) + sideMenuContainer.appendChild(sideMenu) - sideMenuOpener.insertBefore('#nextcloud') + nextcloud.parentNode.insertBefore(sideMenuOpener, nextcloud) - sideMenuOpener.insertAfter('#nextcloud') + nextcloud.parentNode.insertBefore(sideMenuOpener, nextcloud.nextSibling) - var topMenuApps = ; + const topMenuApps = From a074d747c19cbc8a66a13fa0e7f088b05e0adeff Mon Sep 17 00:00:00 2001 From: Simon Vieille Date: Fri, 29 Oct 2021 11:14:27 +0200 Subject: [PATCH 013/114] remove most of jquery from top menu --- templates/js/_topMenuApps.js | 221 ++++++++++++++++++++--------------- templates/js/script.php | 26 +++-- 2 files changed, 142 insertions(+), 105 deletions(-) diff --git a/templates/js/_topMenuApps.js b/templates/js/_topMenuApps.js index 57b7088..589c464 100644 --- a/templates/js/_topMenuApps.js +++ b/templates/js/_topMenuApps.js @@ -1,32 +1,55 @@ -var menuCache = null +let menuCache = null -var updateTopMenu = function() { - var breakpointMobileWidth = 1024 - var menu = jQuery('#appmenu') - var apps = menu.find('li') - var minAppsDesktop = 8 - var usePercentualAppMenuLimit = 0.8 - var isMobile = jQuery(window).width() < breakpointMobileWidth - var lastShownApp = null - var appShown = [] - var moreApps = jQuery('#more-apps') - var navigation = jQuery('#navigation') - var navigationApps = jQuery('#apps ul') - var appCount = null +const breakpointMobileWidth = 1024 +const usePercentualAppMenuLimit = 0.8 +const minAppsDesktop = 8 - var currentMenuCache = menu.html() + menu.next().html() +const handleMenuClick = (e, icon) => { + let element = e.target - if (currentMenuCache === menuCache) { + while (element.tagName !== 'LI') { + element = element.parentNode + } + + const a = querySelector('a', element) + + if (a.getAttribute('target') !== '_blank' && e.which === 1 && !e.ctrlKey && !e.metaKey) { + for (let tag of ['svg', 'div']) { + let el = querySelector(tag, element) + + if (el) { + el.remove() + } + } + + const loader = createElement('div', {'class': icon}) + + a.insertBefore(loader, querySelector('span', a)) + } +} + +const updateTopMenu = function() { + const isMobile = window.innerWidth < breakpointMobileWidth + const menu = querySelector('#appmenu') + const moreApps = querySelector('#more-apps') + const navigation = querySelector('#navigation') + const navigationApps = querySelector('#apps ul') + + let apps = querySelectorAll('li', menu) + let lastShownApp = null + let appShown = [] + + if ((menu.innerHTML + menu.nextSibling.innerHTML) === menuCache) { return } - navigationApps.html('') + navigationAppsHtml = '' - apps.each(function(i, app) { - var dataId = app.getAttribute('data-id') + for (let app of apps) { + const dataId = app.getAttribute('data-id') if (dataId === null) { - return + continue } if (topMenuApps.indexOf(dataId) === -1) { @@ -36,25 +59,28 @@ var updateTopMenu = function() { app.classList.remove('hidden') app.classList.add('app-external-site') appShown.push(app) - navigationApps.append(app.outerHTML) + + navigationAppsHtml = navigationAppsHtml + app.outerHTML } if (targetBlankApps.indexOf(dataId) !== -1) { - jQuery(app).children('a').attr('target', '_blank'); + querySelector('a', app).setAttribute('target', '_blank') } - }) - - var rightHeaderWidth = jQuery('.header-right').outerWidth() - var headerWidth = jQuery('header').outerWidth() - var availableWidth = headerWidth - jQuery('#nextcloud').outerWidth() - - jQuery('#header .side-menu-opener').outerWidth() - - (rightHeaderWidth > 230 ? rightHeaderWidth : 230) - - if (!isMobile) { - availableWidth = availableWidth * usePercentualAppMenuLimit } - appCount = Math.floor(availableWidth / jQuery('#appmenu li').width()) + navigationApps.innerHTML = navigationAppsHtml + + const rightHeaderWidth = querySelector('.header-right').offsetWidth + const headerWidth = querySelector('header').offsetWidth + + let availableWidth = headerWidth + + availableWidth -= nextcloud.offsetWidth + availableWidth -= querySelector('#header .side-menu-opener').offsetWidth + availableWidth -= rightHeaderWidth > 230 ? rightHeaderWidth : 230 + availableWidth *= isMobile ? usePercentualAppMenuLimit : 1 + + let appCount = Math.floor(availableWidth / querySelector('#appmenu li:not(.hidden)').offsetWidth) if (isMobile && appCount > minAppsDesktop) { appCount = minAppsDesktop @@ -63,110 +89,117 @@ var updateTopMenu = function() { } if (appCount === 0) { - menu.addClass('hidden') + menu.classList.add('hidden') + } else { + menu.classList.remove('hidden') } - menu.removeClass('hidden') - menu.css('opacity', 1) + menu.style.opacity = 1 if (appShown.length - 1 - appCount >= 1) { appCount-- } - moreApps.find('a').removeClass('active') + for (let item of querySelectorAll('a', moreApps)) { + item.classList.remove('active') + } - var k = 0 - var notInHeader = 0 - var name + let k = 0 + let notInHeader = 0 - jQuery(appShown).each(function(i, app) { - app = jQuery(app) - name = app.data('id') + for (let app of appShown) { + const name = app.getAttribute('data-id') + const item = querySelector('#apps li[data-id=' + name + '].app-external-site') if (k < appCount && appCount > 0) { - app.removeClass('hidden') + app.classList.remove('hidden') lastShownApp = app - jQuery('#apps li[data-id=' + name + '].app-external-site').addClass('in-header') + item.classList.add('in-header') } else { - app.addClass('hidden') + app.classList.add('hidden') notInHeader++ - jQuery('#apps li[data-id=' + name + '].app-external-site').removeClass('in-header') + item.classList.remove('in-header') - if (appCount > 0 && app.children('a').hasClass('active')) { - lastShownApp.addClass('hidden') - app.removeClass('hidden') + const a = querySelector('a', app) + + if (appCount > 0 && a.classList.contains('active')) { + lastShownApp.classList.add('hidden') + app.classList.remove('hidden') notInHeader++ - jQuery('#apps li[data-id=' + name + '].app-external-site') - .removeClass('in-header') - .addClass('in-header') + item.classList.add('in-header') } } k++ - }) + } - // Hack for https://github.com/nextcloud/server/blob/23b0b63c213f5b31eecae817ffd4a9e26f6624d0/core/src/components/MainMenu.js#L74-L96 - menu.undelegate('li:not(#more-apps) > a', 'click') - menu.delegate('li:not(#more-apps) > a', 'click', function(e) { - var a = $(e.target) + // Hack for: + // - https://github.com/nextcloud/server/blob/master/core/src/components/MainMenu.js#L97-L119 + // - https://github.com/nextcloud/server/blob/master/core/src/components/MainMenu.js#L97-L119 + jQuery(menu).undelegate('li:not(#more-apps) > a', 'click') + jQuery(navigation).undelegate('a', 'click') - if (!a.is('a')) { - a = a.closest('a') + const confs = [ + { + items: querySelectorAll('#navigation li'), + icon: 'icon-loading-small' + }, + { + items: querySelectorAll('li:not(#more-apps)', menu), + icon: OCA.Theming && OCA.Theming.inverted ? 'icon-loading-small' : 'icon-loading-small-dark' + }, + ] + + for (let conf of confs) { + for (let item of conf.items) { + item.addEventListener('click', (e) => { + handleMenuClick(e, conf.icon) + }) } + } - if (a.attr('target') !== '_blank' && e.which === 1 && !e.ctrlKey && !e.metaKey && a.parent('#more-apps').length === 0) { - a.find('svg').remove() - a.find('div').remove() - a.prepend(jQuery('
').addClass( - OCA.Theming && OCA.Theming.inverted - ? 'icon-loading-small' - : 'icon-loading-small-dark' - )) + for (let app of querySelectorAll('#apps li.app-external-site')) { + const appId = app.getAttribute('data-id') - window.location.href = a.attr('href') - } - }) - - jQuery('#apps li.app-external-site').each(function(i, app) { - app = jQuery(app) - var appId = app.attr('data-id') - - if (app.hasClass('in-header')) { - app.find('svg').find('defs').remove() + if (app.classList.contains('in-header')) { + for (let defs of querySelectorAll('svg defs', app)) { + defs.remove() + } } else { - var svg = app.find('svg'); + const svg = querySelector('svg', app) - if (svg.find('defs').length > 0) { + if (querySelectorAll('svg defs', app).length > 0) { return; } - var defs = ` + const defs = ` ` - svg.prepend(defs) - svg.find('image').attr('filter', `url(#invertMenuMore-${appId})`) + svg.innerHTML = defs + svg.innerHTML - var html = svg.get(0).innerHTML.replace(/fecolormatrix/g, 'feColorMatrix'); + for (let image of querySelectorAll('image', svg)) { + image.setAttribute('filter', `url(#invertMenuMore-${appId})`) + } - svg.html(html) + svg.innerHTML = svg.innerHTML.replace(/fecolormatrix/g, 'feColorMatrix') } - }) - - if (notInHeader === 0) { - moreApps.hide() - navigation.hide() - } else { - moreApps.show() } - menuCache = menu.html() + menu.next().html() + if (notInHeader === 0) { + moreApps.style.display = 'none' + navigation.style.display = 'none' + } else { + moreApps.style.display = 'flex' + } + + menuCache = menu.innerHTML + menu.nextSibling.innerHTML } -setInterval(updateTopMenu, 50) +setInterval(updateTopMenu, 1000) diff --git a/templates/js/script.php b/templates/js/script.php index e32af22..8f22477 100644 --- a/templates/js/script.php +++ b/templates/js/script.php @@ -94,11 +94,15 @@ if ($_['always-displayed']) { body.addEventListener('side-menu.ready', () => { const sideMenu = querySelector('#side-menu') const headerMenuOpener = querySelector('#header .side-menu-opener') - const sideMenuOpener = querySelector('#side-menu .side-menu-opener') + const sideMenuOpener = querySelectorAll('#side-menu .side-menu-opener') sideMenuFocus = () => { let a = querySelector('.side-menu-app.active a', sideMenu) + if (!a) { + return + } + if (a.length === 0) { a = querySelector('.side-menu-app:first-child a', sideMenu) } @@ -108,7 +112,7 @@ if ($_['always-displayed']) { } } - + const sideMenuMouseLeave = () => { sideMenu.classList.remove('open') sideMenu.removeEventListener('mouseleave', sideMenuMouseLeave) @@ -142,20 +146,20 @@ if ($_['always-displayed']) { const a = querySelector('.side-menu-app.active a', sideMenu) - if (a.length) { + if (a !== null) { a.focus() } }) - - sideMenuOpener.addEventListener('click', () => { - sideMenu.classList.toggle('open') + for (let opener of sideMenuOpener) { + opener.addEventListener('click', () => { + + sideMenu.classList.toggle('open') + + sideMenu.classList.remove('open') + }) - - sideMenuOpener.addEventListener('click', () => { - sideMenu.classList.remove('open') - }) - + } document.addEventListener('keydown', (e) => { var key = e.key || e.keyCode From 5d93d02960b1baffce6e5622d83808b3eccb7a1e Mon Sep 17 00:00:00 2001 From: Simon Vieille Date: Fri, 29 Oct 2021 11:24:01 +0200 Subject: [PATCH 014/114] refactoring of alwaysDisplayed function --- templates/js/_alwaysDisplayed.js | 28 +++++++++++++--------------- 1 file changed, 13 insertions(+), 15 deletions(-) diff --git a/templates/js/_alwaysDisplayed.js b/templates/js/_alwaysDisplayed.js index f56b2a2..7a2a36b 100644 --- a/templates/js/_alwaysDisplayed.js +++ b/templates/js/_alwaysDisplayed.js @@ -1,21 +1,19 @@ -var alwaysDisplayed = function() { - var elements = querySelectorAll('*') - var fixedElements = [] - - for (var i in elements) { - var element = elements[i] +const alwaysDisplayed = function() { + const elements = querySelectorAll('*') + const fixedElements = [] + for (var element of elements) { if (typeof element !== 'object') { continue } - var position = window.getComputedStyle(element, null).getPropertyValue('position'); + const position = window.getComputedStyle(element, null).getPropertyValue('position'); if (position !== 'fixed') { continue } - var id = element.getAttribute('id') + const id = element.getAttribute('id') if (id === 'header' || id === 'side-menu' || id === 'side-menu-loader') { continue @@ -47,19 +45,19 @@ var alwaysDisplayed = function() { } for (var i in fixedElements) { - var element = fixedElements[i] - var computedStyle = window.getComputedStyle(element, null) - var left = computedStyle.getPropertyValue('left') - var right = computedStyle.getPropertyValue('right') + const element = fixedElements[i] + const computedStyle = window.getComputedStyle(element, null) + const left = computedStyle.getPropertyValue('left') + const right = computedStyle.getPropertyValue('right') if (right !== '0px') { - var intValue = parseInt(left.replace('px', '')) - element.style.setProperty('transform', 'translateX(' + (intValue + 50) + 'px)') + const intValue = parseInt(left.replace('px', '')) + 50 + element.style.setProperty('transform', 'translateX(' + intValue.toString() + 'px)') } } } -let content = document.getElementById('content') +const content = querySelector('#content') if (content && content.classList.contains('app-settings')) { let loaded = false From c2e6d24b95e16c420b5af38218bb081925a00b11 Mon Sep 17 00:00:00 2001 From: Simon Vieille Date: Fri, 29 Oct 2021 11:27:45 +0200 Subject: [PATCH 015/114] remove ';' on endline --- src/SideMenu.vue | 6 +++--- src/SideMenuBig.vue | 6 +++--- src/SideMenuWithCategories.vue | 6 +++--- templates/js/_alwaysDisplayed.js | 2 +- templates/js/_topMenuApps.js | 6 +++--- 5 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/SideMenu.vue b/src/SideMenu.vue index 908f84c..7ab3cab 100644 --- a/src/SideMenu.vue +++ b/src/SideMenu.vue @@ -122,7 +122,7 @@ export default { name: trim(element.querySelector('span').innerHTML), icon: svg, active: element.classList.contains('active') - }); + }) } } @@ -130,7 +130,7 @@ export default { window.setTimeout(function() { document.querySelector('body').dispatchEvent(new CustomEvent('side-menu.apps', { detail: {apps: apps}, - })); + })) }, 1000) })(this.apps) }, @@ -149,7 +149,7 @@ export default { that.logo = config['logo'] that.logoLink = config['logo-link'] that.settings = config['settings'] - }); + }) }, }, mounted() { diff --git a/src/SideMenuBig.vue b/src/SideMenuBig.vue index 31b9992..72e6321 100644 --- a/src/SideMenuBig.vue +++ b/src/SideMenuBig.vue @@ -99,8 +99,8 @@ export default { document.querySelector('body').dispatchEvent(new CustomEvent('side-menu.apps', { detail: {apps: apps}, - })); - }); + })) + }) }, retrieveActiveApp() { @@ -118,7 +118,7 @@ export default { that.targetBlankApps = config['target-blank-apps'] that.settings = config['settings'] - }); + }) }, }, mounted() { diff --git a/src/SideMenuWithCategories.vue b/src/SideMenuWithCategories.vue index 0ba9c45..7accbf0 100644 --- a/src/SideMenuWithCategories.vue +++ b/src/SideMenuWithCategories.vue @@ -97,8 +97,8 @@ export default { document.querySelector('body').dispatchEvent(new CustomEvent('side-menu.apps', { detail: {apps: apps}, - })); - }); + })) + }) }, retrieveActiveApp() { @@ -116,7 +116,7 @@ export default { that.targetBlankApps = config['target-blank-apps'] that.settings = config['settings'] - }); + }) }, }, mounted() { diff --git a/templates/js/_alwaysDisplayed.js b/templates/js/_alwaysDisplayed.js index 7a2a36b..e37230a 100644 --- a/templates/js/_alwaysDisplayed.js +++ b/templates/js/_alwaysDisplayed.js @@ -7,7 +7,7 @@ const alwaysDisplayed = function() { continue } - const position = window.getComputedStyle(element, null).getPropertyValue('position'); + const position = window.getComputedStyle(element, null).getPropertyValue('position') if (position !== 'fixed') { continue diff --git a/templates/js/_topMenuApps.js b/templates/js/_topMenuApps.js index 589c464..9a6af72 100644 --- a/templates/js/_topMenuApps.js +++ b/templates/js/_topMenuApps.js @@ -144,11 +144,11 @@ const updateTopMenu = function() { const confs = [ { - items: querySelectorAll('#navigation li'), + items: querySelectorAll('#navigation li'), icon: 'icon-loading-small' }, { - items: querySelectorAll('li:not(#more-apps)', menu), + items: querySelectorAll('li:not(#more-apps)', menu), icon: OCA.Theming && OCA.Theming.inverted ? 'icon-loading-small' : 'icon-loading-small-dark' }, ] @@ -172,7 +172,7 @@ const updateTopMenu = function() { const svg = querySelector('svg', app) if (querySelectorAll('svg defs', app).length > 0) { - return; + return } const defs = ` From 6f0c4e98e0c72d1b01de1c6e1fcd0d03baeffb5a Mon Sep 17 00:00:00 2001 From: Simon Vieille Date: Fri, 29 Oct 2021 13:56:48 +0200 Subject: [PATCH 016/114] fix top menu generation; fix issue #66 by removing usage of setInterval --- templates/js/_topMenuApps.js | 40 +++++++++++++++++++++++------------- 1 file changed, 26 insertions(+), 14 deletions(-) diff --git a/templates/js/_topMenuApps.js b/templates/js/_topMenuApps.js index 9a6af72..188382f 100644 --- a/templates/js/_topMenuApps.js +++ b/templates/js/_topMenuApps.js @@ -1,4 +1,6 @@ let menuCache = null +let updateTopMenuIsRunning = false +let forceUpdateTopMenu = false const breakpointMobileWidth = 1024 const usePercentualAppMenuLimit = 0.8 @@ -58,6 +60,7 @@ const updateTopMenu = function() { } else { app.classList.remove('hidden') app.classList.add('app-external-site') + appShown.push(app) navigationAppsHtml = navigationAppsHtml + app.outerHTML @@ -66,6 +69,7 @@ const updateTopMenu = function() { if (targetBlankApps.indexOf(dataId) !== -1) { querySelector('a', app).setAttribute('target', '_blank') } + } navigationApps.innerHTML = navigationAppsHtml @@ -88,12 +92,6 @@ const updateTopMenu = function() { appCount = minAppsDesktop } - if (appCount === 0) { - menu.classList.add('hidden') - } else { - menu.classList.remove('hidden') - } - menu.style.opacity = 1 if (appShown.length - 1 - appCount >= 1) { @@ -107,20 +105,21 @@ const updateTopMenu = function() { let k = 0 let notInHeader = 0 + for (let app of appShown) { const name = app.getAttribute('data-id') - const item = querySelector('#apps li[data-id=' + name + '].app-external-site') + const li = querySelector('#apps li[data-id=' + name + '].app-external-site') if (k < appCount && appCount > 0) { app.classList.remove('hidden') - lastShownApp = app + li.classList.add('in-header') - item.classList.add('in-header') + lastShownApp = app } else { app.classList.add('hidden') - notInHeader++ + li.classList.remove('in-header') - item.classList.remove('in-header') + notInHeader++ const a = querySelector('a', app) @@ -129,7 +128,7 @@ const updateTopMenu = function() { app.classList.remove('hidden') notInHeader++ - item.classList.add('in-header') + li.classList.add('in-header') } } @@ -172,7 +171,7 @@ const updateTopMenu = function() { const svg = querySelector('svg', app) if (querySelectorAll('svg defs', app).length > 0) { - return + continue } const defs = ` @@ -202,4 +201,17 @@ const updateTopMenu = function() { menuCache = menu.innerHTML + menu.nextSibling.innerHTML } -setInterval(updateTopMenu, 1000) + +for (let timeout of [300, 500, 700]) { + setTimeout(updateTopMenu, timeout) +} + +let resizeTimeout = null; + +window.addEventListener('resize', () => { + if (resizeTimeout !== null) { + clearTimeout(resizeTimeout) + } + + resizeTimeout = setTimeout(updateTopMenu, 100) +}) From 96ffdf93ea099f44cc187c6a39bd6533f1a2b26e Mon Sep 17 00:00:00 2001 From: Simon Vieille Date: Fri, 29 Oct 2021 15:30:22 +0200 Subject: [PATCH 017/114] remove useless vars --- templates/js/_topMenuApps.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/templates/js/_topMenuApps.js b/templates/js/_topMenuApps.js index 188382f..92658a2 100644 --- a/templates/js/_topMenuApps.js +++ b/templates/js/_topMenuApps.js @@ -1,6 +1,4 @@ let menuCache = null -let updateTopMenuIsRunning = false -let forceUpdateTopMenu = false const breakpointMobileWidth = 1024 const usePercentualAppMenuLimit = 0.8 From d1faf881daa8c49ef1e292d5b0a25a0015be1392 Mon Sep 17 00:00:00 2001 From: Simon Vieille Date: Sun, 31 Oct 2021 16:54:16 +0100 Subject: [PATCH 018/114] add timeout --- templates/js/_topMenuApps.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/templates/js/_topMenuApps.js b/templates/js/_topMenuApps.js index 92658a2..dee32c8 100644 --- a/templates/js/_topMenuApps.js +++ b/templates/js/_topMenuApps.js @@ -199,8 +199,7 @@ const updateTopMenu = function() { menuCache = menu.innerHTML + menu.nextSibling.innerHTML } - -for (let timeout of [300, 500, 700]) { +for (let timeout of [300, 500, 700, 900, 1100]) { setTimeout(updateTopMenu, timeout) } From 7471e93847ef72235bc82c18c8bdf7fad282b6d5 Mon Sep 17 00:00:00 2001 From: Simon Vieille Date: Sun, 31 Oct 2021 16:55:04 +0100 Subject: [PATCH 019/114] fix null setting update --- templates/settings/admin-form.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/settings/admin-form.php b/templates/settings/admin-form.php index 0f2e509..30fc3a9 100644 --- a/templates/settings/admin-form.php +++ b/templates/settings/admin-form.php @@ -799,7 +799,7 @@ $choicesSizes = [ 🖱️ t('Show and hide the list of categories')); ?> -
@@ -244,10 +255,10 @@ $choicesSizes = [ class="side-menu-setting" value=""> - + ])) ?>">
@@ -278,10 +289,15 @@ $choicesSizes = [
+ +
@@ -293,10 +309,15 @@ $choicesSizes = [
+ +
@@ -308,10 +329,15 @@ $choicesSizes = [
+ +
From 814567635d4a70b841e439cacd5a0c41765f6518 Mon Sep 17 00:00:00 2001 From: Simon Vieille Date: Tue, 31 May 2022 16:29:29 +0200 Subject: [PATCH 091/114] update changelog --- CHANGELOG.md | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9b050c5..e357e03 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,20 @@ ## [Unreleased] +## 2.4.0 +### Added +* remove focus on opener after click +* add button to set default colors +* add menu hover effect +### Fixed +* fix deprecated app.php file +* fix menu with categories header +* fix minor issues +### Changed +* change saving progression +### Removed +* Nextcloud 19 is not supported anymore +* PHP 7.3 is not supported anymore + ## 2.3.5 ### Fixed * fix white square (#99) From 6b1a995d5d030eb54894779ddb276b8b2a60a9af Mon Sep 17 00:00:00 2001 From: Simon Vieille Date: Tue, 31 May 2022 16:43:49 +0200 Subject: [PATCH 092/114] add tooltips --- src/admin.js | 2 ++ src/l10n/fixtures/cs.yaml | 1 + src/l10n/fixtures/de.yaml | 1 + src/l10n/fixtures/fr.yaml | 1 + src/l10n/fixtures/zh_CN.yaml | 1 + templates/settings/admin-form.php | 16 ++++++++-------- 6 files changed, 14 insertions(+), 8 deletions(-) diff --git a/src/admin.js b/src/admin.js index 51573ab..9231c9a 100644 --- a/src/admin.js +++ b/src/admin.js @@ -134,6 +134,8 @@ const updateAppsCategoriesCustom = () => { } document.addEventListener('DOMContentLoaded', () => { + $('*[data-toggle="tooltip"]').tooltip(); + if (document.querySelector('#side-menu-categories-custom')) { const View = Vue.extend(AdminCategoriesCustom) const adminCategoriesCustom = new View({}) diff --git a/src/l10n/fixtures/cs.yaml b/src/l10n/fixtures/cs.yaml index 11fc4b1..13a1b0f 100644 --- a/src/l10n/fixtures/cs.yaml +++ b/src/l10n/fixtures/cs.yaml @@ -82,3 +82,4 @@ "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" diff --git a/src/l10n/fixtures/de.yaml b/src/l10n/fixtures/de.yaml index eb30f55..fe2567d 100644 --- a/src/l10n/fixtures/de.yaml +++ b/src/l10n/fixtures/de.yaml @@ -81,3 +81,4 @@ "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" diff --git a/src/l10n/fixtures/fr.yaml b/src/l10n/fixtures/fr.yaml index 9b6a6c0..ec5efd5 100644 --- a/src/l10n/fixtures/fr.yaml +++ b/src/l10n/fixtures/fr.yaml @@ -81,3 +81,4 @@ "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" diff --git a/src/l10n/fixtures/zh_CN.yaml b/src/l10n/fixtures/zh_CN.yaml index 294157a..8ed069c 100644 --- a/src/l10n/fixtures/zh_CN.yaml +++ b/src/l10n/fixtures/zh_CN.yaml @@ -81,3 +81,4 @@ "Customize application categories": "自定义应用程序类别" "Apps only visible in the top menu": "应用程序仅在顶部菜单中可见" "Apps visible in the top and side menus": "顶部和侧边菜单中可见的应用程序" +"Reset to default": "重置为默认设置" diff --git a/templates/settings/admin-form.php b/templates/settings/admin-form.php index b0b2514..651ad6a 100644 --- a/templates/settings/admin-form.php +++ b/templates/settings/admin-form.php @@ -67,7 +67,7 @@ $choicesSizes = [ class="side-menu-setting side-menu-setting-live" value=""> -
@@ -107,7 +107,7 @@ $choicesSizes = [ class="side-menu-setting side-menu-setting-live" value=""> -
@@ -127,7 +127,7 @@ $choicesSizes = [ class="side-menu-setting side-menu-setting-live" value=""> -
@@ -147,7 +147,7 @@ $choicesSizes = [ class="side-menu-setting" value=""> -
@@ -255,7 +255,7 @@ $choicesSizes = [ class="side-menu-setting" value=""> -
@@ -295,7 +295,7 @@ $choicesSizes = [ class="side-menu-setting" value=""> -
@@ -315,7 +315,7 @@ $choicesSizes = [ class="side-menu-setting" value=""> -
@@ -335,7 +335,7 @@ $choicesSizes = [ class="side-menu-setting" value=""> -
From 7b2f64c5cd562624e77402eed5b1be576c58d727 Mon Sep 17 00:00:00 2001 From: Simon Vieille Date: Tue, 31 May 2022 21:11:11 +0200 Subject: [PATCH 093/114] add translations --- src/l10n/fixtures/cs.yaml | 8 ++++++++ src/l10n/fixtures/de.yaml | 8 ++++++++ src/l10n/fixtures/fr.yaml | 8 ++++++++ src/l10n/fixtures/zh_CN.yaml | 8 ++++++++ templates/settings/admin-form.php | 4 ++-- 5 files changed, 34 insertions(+), 2 deletions(-) diff --git a/src/l10n/fixtures/cs.yaml b/src/l10n/fixtures/cs.yaml index 13a1b0f..6958c09 100644 --- a/src/l10n/fixtures/cs.yaml +++ b/src/l10n/fixtures/cs.yaml @@ -83,3 +83,11 @@ "Apps only visible in the top menu": "Aplikace jsou viditelné pouze v horní nabídce " "Apps visible in the top and side menus": "Aplikace viditelné v horní a boční nabídce" "Reset to default": "Vrátit zpět na výchozí hodnoty" +"Hidden icon": "Skrytá ikona" +"Small icon": "Malá ikona" +"Normal icon": "Normální ikona" +"Big icon": "Velká ikona" +"Hidden text": "Skrytý text" +"Small text": "Malý text" +"Normal text": "Normální text" +"Big text": "Velký text" diff --git a/src/l10n/fixtures/de.yaml b/src/l10n/fixtures/de.yaml index fe2567d..afffdbb 100644 --- a/src/l10n/fixtures/de.yaml +++ b/src/l10n/fixtures/de.yaml @@ -82,3 +82,11 @@ "Apps only visible in the top menu": "Apps nur im oberen Menü sichtbar " "Apps visible in the top and side menus": "Apps im oberen und seitlichen Menü sichtbar" "Reset to default": "Auf Standard zurücksetzen" +"Hidden icon": "Verstecktes Symbol" +"Small icon": "Kleines Symbol" +"Normal icon": "Normales Symbol" +"Big icon": "Große Ikone" +"Hidden text": "Versteckter Text" +"Small text": "Kleiner Text" +"Normal text": "Normaler Text" +"Big text": "Großer Text" diff --git a/src/l10n/fixtures/fr.yaml b/src/l10n/fixtures/fr.yaml index ec5efd5..3d8fece 100644 --- a/src/l10n/fixtures/fr.yaml +++ b/src/l10n/fixtures/fr.yaml @@ -15,6 +15,14 @@ "Small": "Petit" "Normal": "Normal" "Big": "Gros" +"Hidden icon": "Icône masqué" +"Small icon": "Petit icône" +"Normal icon": "Icône normal" +"Big icon": "Gros icône" +"Hidden text": "Text masqué" +"Small text": "Texte petit" +"Normal text": "Texte normal" +"Big text": "Gros texte" "Colors": "Couleurs" "Background color": "Couleur de fond" "Background color of current app": "Couleur de fond de l'application en cours" diff --git a/src/l10n/fixtures/zh_CN.yaml b/src/l10n/fixtures/zh_CN.yaml index 8ed069c..4e8cb36 100644 --- a/src/l10n/fixtures/zh_CN.yaml +++ b/src/l10n/fixtures/zh_CN.yaml @@ -82,3 +82,11 @@ "Apps only visible in the top menu": "应用程序仅在顶部菜单中可见" "Apps visible in the top and side menus": "顶部和侧边菜单中可见的应用程序" "Reset to default": "重置为默认设置" +"Hidden icon": "隐藏图标" +"Small icon": "小图标" +"Normal icon": "正常图标" +"Big icon": "大图标" +"Hidden text": "隐藏文字" +"Small text": "小文本" +"Normal text": "普通文本" +"Big text": "大文本" diff --git a/templates/settings/admin-form.php b/templates/settings/admin-form.php index 651ad6a..ba7cf77 100644 --- a/templates/settings/admin-form.php +++ b/templates/settings/admin-form.php @@ -697,7 +697,7 @@ $choicesSizes = [ @@ -705,7 +705,7 @@ $choicesSizes = [ From a0be3757e9dc67ad7d585002759804744cd84d5b Mon Sep 17 00:00:00 2001 From: Simon Vieille Date: Tue, 31 May 2022 21:21:30 +0200 Subject: [PATCH 094/114] update screenshots --- screenshots/admin_settings.png | Bin 259731 -> 328842 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/screenshots/admin_settings.png b/screenshots/admin_settings.png index 31d225c44757bf0470f46a3b337bde039012c877..ba7a74421ddbd42f64e130259972303ee369ca5b 100644 GIT binary patch literal 328842 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