mirror of https://github.com/nextcloud/server
Start theming providers
Signed-off-by: Joas Schilling <coding@schilljs.com>
This commit is contained in:
parent
12ed5c9ff3
commit
b3cf312edc
|
@ -44,8 +44,8 @@ return ['routes' => [
|
|||
'verb' => 'POST'
|
||||
],
|
||||
[
|
||||
'name' => 'Theming#getStylesheet',
|
||||
'url' => '/styles',
|
||||
'name' => 'Theming#getThemeVariables',
|
||||
'url' => '/theme/{themeId}.css',
|
||||
'verb' => 'GET',
|
||||
],
|
||||
[
|
||||
|
|
|
@ -100,6 +100,7 @@
|
|||
margin-top: 10px;
|
||||
margin-bottom: 20px;
|
||||
cursor: pointer;
|
||||
background-image: var(--image-login);
|
||||
|
||||
#theming-preview-logo {
|
||||
cursor: pointer;
|
||||
|
@ -110,6 +111,7 @@
|
|||
background-position: center;
|
||||
background-repeat: no-repeat;
|
||||
background-size: contain;
|
||||
background-image: var(--image-logo);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -28,9 +28,9 @@ function setThemingValue(setting, value) {
|
|||
startLoading();
|
||||
$.post(
|
||||
OC.generateUrl('/apps/theming/ajax/updateStylesheet'), {'setting' : setting, 'value' : value}
|
||||
).done(function(response) {
|
||||
).done(function() {
|
||||
hideUndoButton(setting, value);
|
||||
preview(setting, value, response.data.serverCssUrl);
|
||||
preview(setting, value);
|
||||
}).fail(function(response) {
|
||||
OC.msg.finishedSaving('#theming_settings_msg', response.responseJSON);
|
||||
$('#theming_settings_loading').hide();
|
||||
|
@ -39,41 +39,31 @@ function setThemingValue(setting, value) {
|
|||
|
||||
function preview(setting, value, serverCssUrl) {
|
||||
OC.msg.startAction('#theming_settings_msg', t('theming', 'Loading preview…'));
|
||||
var stylesheetsLoaded = 1;
|
||||
var reloadStylesheets = function(cssFile) {
|
||||
var queryString = '?reload=' + new Date().getTime();
|
||||
var url = cssFile + queryString;
|
||||
var old = $('link[href*="' + cssFile + '"]');
|
||||
var stylesheet = $("<link/>", {
|
||||
rel: "stylesheet",
|
||||
type: "text/css",
|
||||
href: url
|
||||
});
|
||||
stylesheet.load(function () {
|
||||
$(old).remove();
|
||||
stylesheetsLoaded--;
|
||||
if(stylesheetsLoaded === 0) {
|
||||
$('#theming_settings_loading').hide();
|
||||
var response = { status: 'success', data: {message: t('theming', 'Saved')}};
|
||||
OC.msg.finishedSaving('#theming_settings_msg', response);
|
||||
|
||||
// Get all theming themes css links and force reload them
|
||||
[...document.querySelectorAll('link.theme')]
|
||||
.forEach(theme => {
|
||||
// Only edit the clone to not remove applied one
|
||||
var clone = theme.cloneNode()
|
||||
var url = new URL(clone.href)
|
||||
// Set current timestamp as cache buster
|
||||
url.searchParams.set('v', Date.now())
|
||||
clone.href = url.toString()
|
||||
clone.onload = function() {
|
||||
theme.remove()
|
||||
}
|
||||
});
|
||||
stylesheet.appendTo("head");
|
||||
};
|
||||
|
||||
if (serverCssUrl !== undefined) {
|
||||
stylesheetsLoaded++;
|
||||
|
||||
reloadStylesheets(serverCssUrl);
|
||||
}
|
||||
reloadStylesheets(OC.generateUrl('/apps/theming/styles'));
|
||||
document.head.append(clone)
|
||||
})
|
||||
|
||||
if (setting === 'name') {
|
||||
window.document.title = t('core', 'Admin') + " - " + value;
|
||||
}
|
||||
|
||||
|
||||
// Finish
|
||||
$('#theming_settings_loading').hide();
|
||||
var response = { status: 'success', data: {message: t('theming', 'Saved')}};
|
||||
OC.msg.finishedSaving('#theming_settings_msg', response);
|
||||
hideUndoButton(setting, value);
|
||||
|
||||
}
|
||||
|
||||
function hideUndoButton(setting, value) {
|
||||
|
|
|
@ -37,12 +37,13 @@
|
|||
*/
|
||||
namespace OCA\Theming\Controller;
|
||||
|
||||
use OC\Template\SCSSCacher;
|
||||
use OCA\Theming\ImageManager;
|
||||
use OCA\Theming\Service\ThemesService;
|
||||
use OCA\Theming\ThemingDefaults;
|
||||
use OCP\App\IAppManager;
|
||||
use OCP\AppFramework\Controller;
|
||||
use OCP\AppFramework\Http;
|
||||
use OCP\AppFramework\Http\DataDisplayResponse;
|
||||
use OCP\AppFramework\Http\DataResponse;
|
||||
use OCP\AppFramework\Http\FileDisplayResponse;
|
||||
use OCP\AppFramework\Http\NotFoundResponse;
|
||||
|
@ -63,40 +64,16 @@ use OCP\IURLGenerator;
|
|||
* @package OCA\Theming\Controller
|
||||
*/
|
||||
class ThemingController extends Controller {
|
||||
/** @var ThemingDefaults */
|
||||
private $themingDefaults;
|
||||
/** @var IL10N */
|
||||
private $l10n;
|
||||
/** @var IConfig */
|
||||
private $config;
|
||||
/** @var ITempManager */
|
||||
private $tempManager;
|
||||
/** @var IAppData */
|
||||
private $appData;
|
||||
/** @var SCSSCacher */
|
||||
private $scssCacher;
|
||||
/** @var IURLGenerator */
|
||||
private $urlGenerator;
|
||||
/** @var IAppManager */
|
||||
private $appManager;
|
||||
/** @var ImageManager */
|
||||
private $imageManager;
|
||||
private ThemingDefaults $themingDefaults;
|
||||
private IL10N $l10n;
|
||||
private IConfig $config;
|
||||
private ITempManager $tempManager;
|
||||
private IAppData $appData;
|
||||
private IURLGenerator $urlGenerator;
|
||||
private IAppManager $appManager;
|
||||
private ImageManager $imageManager;
|
||||
private ThemesService $themesService;
|
||||
|
||||
/**
|
||||
* ThemingController constructor.
|
||||
*
|
||||
* @param string $appName
|
||||
* @param IRequest $request
|
||||
* @param IConfig $config
|
||||
* @param ThemingDefaults $themingDefaults
|
||||
* @param IL10N $l
|
||||
* @param ITempManager $tempManager
|
||||
* @param IAppData $appData
|
||||
* @param SCSSCacher $scssCacher
|
||||
* @param IURLGenerator $urlGenerator
|
||||
* @param IAppManager $appManager
|
||||
* @param ImageManager $imageManager
|
||||
*/
|
||||
public function __construct(
|
||||
$appName,
|
||||
IRequest $request,
|
||||
|
@ -105,10 +82,10 @@ class ThemingController extends Controller {
|
|||
IL10N $l,
|
||||
ITempManager $tempManager,
|
||||
IAppData $appData,
|
||||
SCSSCacher $scssCacher,
|
||||
IURLGenerator $urlGenerator,
|
||||
IAppManager $appManager,
|
||||
ImageManager $imageManager
|
||||
ImageManager $imageManager,
|
||||
ThemesService $themesService
|
||||
) {
|
||||
parent::__construct($appName, $request);
|
||||
|
||||
|
@ -117,10 +94,10 @@ class ThemingController extends Controller {
|
|||
$this->config = $config;
|
||||
$this->tempManager = $tempManager;
|
||||
$this->appData = $appData;
|
||||
$this->scssCacher = $scssCacher;
|
||||
$this->urlGenerator = $urlGenerator;
|
||||
$this->appManager = $appManager;
|
||||
$this->imageManager = $imageManager;
|
||||
$this->themesService = $themesService;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -185,19 +162,12 @@ class ThemingController extends Controller {
|
|||
|
||||
$this->themingDefaults->set($setting, $value);
|
||||
|
||||
// reprocess server scss for preview
|
||||
$cssCached = $this->scssCacher->process(\OC::$SERVERROOT, 'core/css/css-variables.scss', 'core');
|
||||
|
||||
return new DataResponse(
|
||||
[
|
||||
'data' =>
|
||||
[
|
||||
'message' => $this->l10n->t('Saved'),
|
||||
'serverCssUrl' => $this->urlGenerator->linkTo('', $this->scssCacher->getCachedSCSS('core', '/core/css/css-variables.scss'))
|
||||
],
|
||||
'status' => 'success'
|
||||
]
|
||||
);
|
||||
return new DataResponse([
|
||||
'data' => [
|
||||
'message' => $this->l10n->t('Saved'),
|
||||
],
|
||||
'status' => 'success'
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -262,7 +232,6 @@ class ThemingController extends Controller {
|
|||
}
|
||||
|
||||
$name = $image['name'];
|
||||
$cssCached = $this->scssCacher->process(\OC::$SERVERROOT, 'core/css/css-variables.scss', 'core');
|
||||
|
||||
return new DataResponse(
|
||||
[
|
||||
|
@ -271,7 +240,6 @@ class ThemingController extends Controller {
|
|||
'name' => $name,
|
||||
'url' => $this->imageManager->getImageUrl($key),
|
||||
'message' => $this->l10n->t('Saved'),
|
||||
'serverCssUrl' => $this->urlGenerator->linkTo('', $this->scssCacher->getCachedSCSS('core', '/core/css/css-variables.scss'))
|
||||
],
|
||||
'status' => 'success'
|
||||
]
|
||||
|
@ -288,8 +256,6 @@ class ThemingController extends Controller {
|
|||
*/
|
||||
public function undo(string $setting): DataResponse {
|
||||
$value = $this->themingDefaults->undo($setting);
|
||||
// reprocess server scss for preview
|
||||
$cssCached = $this->scssCacher->process(\OC::$SERVERROOT, 'core/css/css-variables.scss', 'core');
|
||||
|
||||
return new DataResponse(
|
||||
[
|
||||
|
@ -297,7 +263,6 @@ class ThemingController extends Controller {
|
|||
[
|
||||
'value' => $value,
|
||||
'message' => $this->l10n->t('Saved'),
|
||||
'serverCssUrl' => $this->urlGenerator->linkTo('', $this->scssCacher->getCachedSCSS('core', '/core/css/css-variables.scss'))
|
||||
],
|
||||
'status' => 'success'
|
||||
]
|
||||
|
@ -341,25 +306,31 @@ class ThemingController extends Controller {
|
|||
* @NoSameSiteCookieRequired
|
||||
*
|
||||
* @return FileDisplayResponse|NotFoundResponse
|
||||
* @throws NotPermittedException
|
||||
* @throws \Exception
|
||||
* @throws \OCP\App\AppPathNotFoundException
|
||||
*/
|
||||
public function getStylesheet() {
|
||||
$appPath = $this->appManager->getAppPath('theming');
|
||||
|
||||
/* SCSSCacher is required here
|
||||
* We cannot rely on automatic caching done by \OC_Util::addStyle,
|
||||
* since we need to add the cacheBuster value to the url
|
||||
*/
|
||||
$cssCached = $this->scssCacher->process($appPath, 'css/theming.scss', 'theming');
|
||||
if (!$cssCached) {
|
||||
public function getThemeVariables(string $themeId, bool $plain = false) {
|
||||
$themes = $this->themesService->getThemes();
|
||||
if (!in_array($themeId, array_keys($themes))) {
|
||||
return new NotFoundResponse();
|
||||
}
|
||||
|
||||
$theme = $themes[$themeId];
|
||||
|
||||
// Generate variables
|
||||
$variables = '';
|
||||
foreach ($theme->getCSSVariables() as $variable => $value) {
|
||||
$variables .= "$variable:$value; ";
|
||||
};
|
||||
|
||||
// If plain is set, the browser decides of the css priority
|
||||
if ($plain) {
|
||||
$css = ":root { $variables }";
|
||||
} else {
|
||||
// If not set, we'll rely on the body class
|
||||
$css = "body[data-theme-$themeId] { $variables }";
|
||||
}
|
||||
|
||||
try {
|
||||
$cssFile = $this->scssCacher->getCachedCSS('theming', 'theming.css');
|
||||
$response = new FileDisplayResponse($cssFile, Http::STATUS_OK, ['Content-Type' => 'text/css']);
|
||||
$response = new DataDisplayResponse($css, Http::STATUS_OK, ['Content-Type' => 'text/css']);
|
||||
$response->cacheFor(86400);
|
||||
return $response;
|
||||
} catch (NotFoundException $e) {
|
||||
|
|
|
@ -0,0 +1,55 @@
|
|||
<?php
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* @copyright Copyright (c) 2022 John Molakvoæ <skjnldsv@protonmail.com>
|
||||
*
|
||||
* @author John Molakvoæ <skjnldsv@protonmail.com>
|
||||
*
|
||||
* @license GNU AGPL version 3 or any later version
|
||||
*
|
||||
* This code is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License, version 3,
|
||||
* as published by the Free Software Foundation.
|
||||
*
|
||||
* 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, version 3,
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
*
|
||||
*/
|
||||
|
||||
namespace OCA\Theming;
|
||||
|
||||
/**
|
||||
* Interface ITheme
|
||||
*
|
||||
* @since 25.0.0
|
||||
*/
|
||||
interface ITheme {
|
||||
|
||||
/**
|
||||
* Unique theme id
|
||||
* @since 25.0.0
|
||||
*/
|
||||
public function getId(): string;
|
||||
|
||||
/**
|
||||
* Get the media query triggering this theme
|
||||
* Optional, ignored if falsy
|
||||
*
|
||||
* @return string
|
||||
* @since 25.0.0
|
||||
*/
|
||||
public function getMediaQuery(): string;
|
||||
|
||||
/**
|
||||
* Return the list of changed css variables
|
||||
*
|
||||
* @return array
|
||||
* @since 25.0.0
|
||||
*/
|
||||
public function getCSSVariables(): array;
|
||||
}
|
|
@ -27,6 +27,8 @@ namespace OCA\Theming\Listener;
|
|||
|
||||
use OCA\Theming\AppInfo\Application;
|
||||
use OCA\Theming\Service\JSDataService;
|
||||
use OCA\Theming\Service\ThemeInjectionService;
|
||||
use OCA\Theming\Service\ThemesService;
|
||||
use OCP\EventDispatcher\Event;
|
||||
use OCP\EventDispatcher\IEventListener;
|
||||
use OCP\IConfig;
|
||||
|
@ -36,25 +38,18 @@ use OCP\IURLGenerator;
|
|||
|
||||
class BeforeTemplateRenderedListener implements IEventListener {
|
||||
|
||||
/** @var IInitialStateService */
|
||||
private $initialStateService;
|
||||
/** @var IURLGenerator */
|
||||
private $urlGenerator;
|
||||
/** @var IConfig */
|
||||
private $config;
|
||||
/** @var IServerContainer */
|
||||
private $serverContainer;
|
||||
private IInitialStateService $initialStateService;
|
||||
private IServerContainer $serverContainer;
|
||||
private ThemeInjectionService $themeInjectionService;
|
||||
|
||||
public function __construct(
|
||||
IInitialStateService $initialStateService,
|
||||
IURLGenerator $urlGenerator,
|
||||
IConfig $config,
|
||||
IServerContainer $serverContainer
|
||||
IServerContainer $serverContainer,
|
||||
ThemeInjectionService $themeInjectionService
|
||||
) {
|
||||
$this->initialStateService = $initialStateService;
|
||||
$this->urlGenerator = $urlGenerator;
|
||||
$this->config = $config;
|
||||
$this->serverContainer = $serverContainer;
|
||||
$this->themeInjectionService = $themeInjectionService;
|
||||
}
|
||||
|
||||
public function handle(Event $event): void {
|
||||
|
@ -63,19 +58,21 @@ class BeforeTemplateRenderedListener implements IEventListener {
|
|||
return $serverContainer->query(JSDataService::class);
|
||||
});
|
||||
|
||||
$linkToCSS = $this->urlGenerator->linkToRoute(
|
||||
'theming.Theming.getStylesheet',
|
||||
[
|
||||
'v' => $this->config->getAppValue('theming', 'cachebuster', '0'),
|
||||
]
|
||||
);
|
||||
\OCP\Util::addHeader(
|
||||
'link',
|
||||
[
|
||||
'rel' => 'stylesheet',
|
||||
'href' => $linkToCSS,
|
||||
]
|
||||
);
|
||||
// $linkToCSS = $this->urlGenerator->linkToRoute(
|
||||
// 'theming.Theming.getStylesheet',
|
||||
// [
|
||||
// 'v' => $this->config->getAppValue('theming', 'cachebuster', '0'),
|
||||
// ]
|
||||
// );
|
||||
// \OCP\Util::addHeader(
|
||||
// 'link',
|
||||
// [
|
||||
// 'rel' => 'stylesheet',
|
||||
// 'href' => $linkToCSS,
|
||||
// ]
|
||||
// );
|
||||
|
||||
$this->themeInjectionService->injectHeaders();
|
||||
|
||||
// Making sure to inject just after core
|
||||
\OCP\Util::addScript('theming', 'theming', 'core');
|
||||
|
|
|
@ -0,0 +1,88 @@
|
|||
<?php
|
||||
/**
|
||||
* @copyright Copyright (c) 2022 John Molakvoæ <skjnldsv@protonmail.com>
|
||||
*
|
||||
* @author John Molakvoæ <skjnldsv@protonmail.com>
|
||||
*
|
||||
* @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/>.
|
||||
*
|
||||
*/
|
||||
namespace OCA\Theming\Service;
|
||||
|
||||
use OCA\Theming\Themes\DefaultTheme;
|
||||
use OCP\IURLGenerator;
|
||||
use OCP\Util;
|
||||
|
||||
class ThemeInjectionService {
|
||||
|
||||
private IURLGenerator $urlGenerator;
|
||||
private ThemesService $themesService;
|
||||
private DefaultTheme $defaultTheme;
|
||||
|
||||
public function __construct(IURLGenerator $urlGenerator,
|
||||
ThemesService $themesService,
|
||||
DefaultTheme $defaultTheme) {
|
||||
$this->urlGenerator = $urlGenerator;
|
||||
$this->themesService = $themesService;
|
||||
$this->defaultTheme = $defaultTheme;
|
||||
}
|
||||
|
||||
public function injectHeaders() {
|
||||
$themes = $this->themesService->getThemes();
|
||||
$defaultTheme = $themes[$this->defaultTheme->getId()];
|
||||
$mediaThemes = array_filter($themes, function($theme) {
|
||||
// Check if the theme provides a media query
|
||||
return (bool)$theme->getMediaQuery();
|
||||
});
|
||||
|
||||
// Default theme fallback
|
||||
$this->addThemeHeader($defaultTheme->getId());
|
||||
|
||||
// Themes applied by media queries
|
||||
foreach($mediaThemes as $theme) {
|
||||
$this->addThemeHeader($theme->getId(), true, $theme->getMediaQuery());
|
||||
}
|
||||
|
||||
// Themes
|
||||
foreach($this->themesService->getThemes() as $theme) {
|
||||
// Ignore default theme as already processed first
|
||||
if ($theme->getId() === $this->defaultTheme->getId()) {
|
||||
continue;
|
||||
}
|
||||
$this->addThemeHeader($theme->getId(), false);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Inject theme header into rendered page
|
||||
*
|
||||
* @param string $themeId the theme ID
|
||||
* @param bool $plain request the :root syntax
|
||||
* @param string $media media query to use in the <link> element
|
||||
*/
|
||||
private function addThemeHeader(string $themeId, bool $plain = true, string $media = null) {
|
||||
$linkToCSS = $this->urlGenerator->linkToRoute('theming.Theming.getThemeVariables', [
|
||||
'themeId' => $themeId,
|
||||
'plain' => $plain,
|
||||
]);
|
||||
Util::addHeader('link', [
|
||||
'rel' => 'stylesheet',
|
||||
'media' => $media,
|
||||
'href' => $linkToCSS,
|
||||
'class' => 'theme'
|
||||
]);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,56 @@
|
|||
<?php
|
||||
/**
|
||||
* @copyright Copyright (c) 2022 John Molakvoæ <skjnldsv@protonmail.com>
|
||||
*
|
||||
* @author John Molakvoæ <skjnldsv@protonmail.com>
|
||||
*
|
||||
* @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/>.
|
||||
*
|
||||
*/
|
||||
namespace OCA\Theming\Service;
|
||||
|
||||
use OCA\Theming\Themes\DefaultTheme;
|
||||
use OCA\Theming\Themes\DarkTheme;
|
||||
use OCA\Theming\Themes\DarkHighContrastTheme;
|
||||
use OCA\Theming\Themes\HighContrastTheme;
|
||||
use OCA\Theming\ITheme;
|
||||
|
||||
class ThemesService {
|
||||
|
||||
/** @var ITheme[] */
|
||||
private array $themesProviders;
|
||||
|
||||
public function __construct(DefaultTheme $defaultTheme,
|
||||
DarkTheme $darkTheme,
|
||||
DarkHighContrastTheme $darkHighContrastTheme,
|
||||
HighContrastTheme $highContrastTheme) {
|
||||
// Register themes
|
||||
$this->themesProviders = [
|
||||
$defaultTheme->getId() => $defaultTheme,
|
||||
$darkTheme->getId() => $darkTheme,
|
||||
$darkHighContrastTheme->getId() => $darkHighContrastTheme,
|
||||
$highContrastTheme->getId() => $highContrastTheme,
|
||||
];
|
||||
}
|
||||
|
||||
public function getThemes() {
|
||||
return $this->themesProviders;
|
||||
}
|
||||
|
||||
public function getThemeVariables(string $id) {
|
||||
return $this->themesProviders[$id]->getCSSVariables();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
<?php
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* @copyright Copyright (c) 2022 Joas Schilling <coding@schilljs.com>
|
||||
*
|
||||
* @author Joas Schilling <coding@schilljs.com>
|
||||
* @author John Molakvoæ <skjnldsv@protonmail.com>
|
||||
*
|
||||
* @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/>.
|
||||
*
|
||||
*/
|
||||
namespace OCA\Theming\Themes;
|
||||
|
||||
use OCA\Theming\ITheme;
|
||||
|
||||
class DarkHighContrastTheme extends HighContrastTheme implements ITheme {
|
||||
|
||||
public function getId(): string {
|
||||
return 'dark-highcontrast';
|
||||
}
|
||||
|
||||
public function getMediaQuery(): string {
|
||||
return '(prefers-color-scheme: dark) and (prefers-contrast: more)';
|
||||
}
|
||||
|
||||
public function getCSSVariables(): array {
|
||||
$variables = parent::getCSSVariables();
|
||||
|
||||
// FIXME …
|
||||
$variables = $variables;
|
||||
|
||||
return $variables;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,75 @@
|
|||
<?php
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* @copyright Copyright (c) 2022 Joas Schilling <coding@schilljs.com>
|
||||
*
|
||||
* @author Joas Schilling <coding@schilljs.com>
|
||||
* @author John Molakvoæ <skjnldsv@protonmail.com>
|
||||
*
|
||||
* @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/>.
|
||||
*
|
||||
*/
|
||||
namespace OCA\Theming\Themes;
|
||||
|
||||
use OCA\Theming\ITheme;
|
||||
|
||||
class DarkTheme extends DefaultTheme implements ITheme {
|
||||
|
||||
public function getId(): string {
|
||||
return 'dark';
|
||||
}
|
||||
|
||||
public function getMediaQuery(): string {
|
||||
return '(prefers-color-scheme: dark)';
|
||||
}
|
||||
|
||||
public function getCSSVariables(): array {
|
||||
$defaultVariables = parent::getCSSVariables();
|
||||
|
||||
$colorMainText = '#D8D8D8';
|
||||
$colorMainBackground = '#171717';
|
||||
$colorMainBackgroundRGB = join(',', $this->util->hexToRGB($colorMainBackground));
|
||||
$colorBoxShadow = $this->util->darken($colorMainBackground, 70);
|
||||
$colorBoxShadowRGB = join(',', $this->util->hexToRGB($colorBoxShadow));
|
||||
|
||||
return array_merge($defaultVariables, [
|
||||
'--color-main-text' => $colorMainText,
|
||||
'--color-main-background' => $colorMainBackground,
|
||||
'--color-main-background-rgb' => $colorMainBackgroundRGB,
|
||||
|
||||
'--color-background-hover' => $this->util->lighten($colorMainBackground, 4),
|
||||
'--color-background-dark' => $this->util->lighten($colorMainBackground, 7),
|
||||
'--color-background-darker' => $this->util->lighten($colorMainBackground, 14),
|
||||
|
||||
'--color-placeholder-light' => $this->util->lighten($colorMainBackground, 10),
|
||||
'--color-placeholder-dark' => $this->util->lighten($colorMainBackground, 20),
|
||||
|
||||
'--color-text-maxcontrast' => $this->util->darken($colorMainText, 30),
|
||||
'--color-text-light' => $this->util->darken($colorMainText, 10),
|
||||
'--color-text-lighter' => $this->util->darken($colorMainText, 20),
|
||||
|
||||
'--color-loading-light' => '#777',
|
||||
'--color-loading-dark' => '#CCC',
|
||||
|
||||
'--color-box-shadow-rgb' => $colorBoxShadowRGB,
|
||||
|
||||
'--color-border' => $this->util->lighten($colorMainBackground, 7),
|
||||
'--color-border-dark' => $this->util->lighten($colorMainBackground, 14),
|
||||
|
||||
'--background-invert-if-bright' => 'invert(100%)',
|
||||
]);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,160 @@
|
|||
<?php
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* @copyright Copyright (c) 2022 Joas Schilling <coding@schilljs.com>
|
||||
*
|
||||
* @author Joas Schilling <coding@schilljs.com>
|
||||
* @author John Molakvoæ <skjnldsv@protonmail.com>
|
||||
*
|
||||
* @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/>.
|
||||
*
|
||||
*/
|
||||
namespace OCA\Theming\Themes;
|
||||
|
||||
use OCA\Theming\ThemingDefaults;
|
||||
use OCA\Theming\Util;
|
||||
use OCA\Theming\ITheme;
|
||||
use OCP\IURLGenerator;
|
||||
|
||||
class DefaultTheme implements ITheme {
|
||||
public Util $util;
|
||||
public ThemingDefaults $themingDefaults;
|
||||
public IURLGenerator $urlGenerator;
|
||||
public string $primaryColor;
|
||||
|
||||
public function __construct(Util $util, ThemingDefaults $themingDefaults, IURLGenerator $urlGenerator) {
|
||||
$this->util = $util;
|
||||
$this->themingDefaults = $themingDefaults;
|
||||
$this->urlGenerator = $urlGenerator;
|
||||
|
||||
$this->primaryColor = $this->themingDefaults->getColorPrimary();
|
||||
}
|
||||
|
||||
public function getId(): string {
|
||||
return 'default';
|
||||
}
|
||||
|
||||
public function getMediaQuery(): string {
|
||||
return '';
|
||||
}
|
||||
|
||||
public function getCSSVariables(): array {
|
||||
$colorMainText = '#222222';
|
||||
$colorMainBackground = '#ffffff';
|
||||
$colorMainBackgroundRGB = join(',', $this->util->hexToRGB($colorMainBackground));
|
||||
$colorBoxShadow = $this->util->darken($colorMainBackground, 70);
|
||||
$colorBoxShadowRGB = join(',', $this->util->hexToRGB($colorBoxShadow));
|
||||
|
||||
// Logo variables
|
||||
$logoSvgPath = $this->urlGenerator->getAbsoluteURL($this->themingDefaults->getLogo());
|
||||
$backgroundSvgPath = $this->urlGenerator->getAbsoluteURL($this->themingDefaults->getBackground());
|
||||
|
||||
return [
|
||||
'--color-main-background' => $colorMainBackground,
|
||||
'--color-main-background-rgb' => $colorMainBackgroundRGB,
|
||||
'--color-main-background-translucent' => 'rgba(var(--color-main-background-rgb), .97)',
|
||||
|
||||
// to use like this: background-image: linear-gradient(0, var('--gradient-main-background));
|
||||
'--gradient-main-background' => 'var(--color-main-background) 0%, var(--color-main-background-translucent) 85%, transparent 100%',
|
||||
|
||||
// used for different active/hover/focus/disabled states
|
||||
'--color-background-hover' => $this->util->darken($colorMainBackground, 4),
|
||||
'--color-background-dark' => $this->util->darken($colorMainBackground, 7),
|
||||
'--color-background-darker' => $this->util->darken($colorMainBackground, 14),
|
||||
|
||||
'--color-placeholder-light' => $this->util->darken($colorMainBackground, 10),
|
||||
'--color-placeholder-dark' => $this->util->darken($colorMainBackground, 20),
|
||||
|
||||
// primary related colours
|
||||
'--color-primary' => $this->primaryColor,
|
||||
'--color-primary-text' => $this->util->invertTextColor($this->primaryColor) ? '#000000' : '#ffffff',
|
||||
'--color-primary-hover' => $this->util->mix($this->primaryColor, $colorMainBackground, 80),
|
||||
'--color-primary-light' => $this->util->mix($this->primaryColor, $colorMainBackground, 10),
|
||||
'--color-primary-light-text' => $this->primaryColor,
|
||||
'--color-primary-light-hover' => $this->util->mix($this->primaryColor, $colorMainText, 10),
|
||||
'--color-primary-text-dark' => $this->util->darken($this->util->invertTextColor($this->primaryColor) ? '#000000' : '#ffffff', 7),
|
||||
// used for buttons, inputs...
|
||||
'--color-primary-element' => $this->util->elementColor($this->primaryColor),
|
||||
'--color-primary-element-hover' => $this->util->mix($this->util->elementColor($this->primaryColor), $colorMainBackground, 80),
|
||||
'--color-primary-element-light' => $this->util->lighten($this->util->elementColor($this->primaryColor), 15),
|
||||
'--color-primary-element-lighter' => $this->util->mix($this->util->elementColor($this->primaryColor), $colorMainBackground, 15),
|
||||
|
||||
// max contrast for WCAG compliance
|
||||
'--color-main-text' => $colorMainText,
|
||||
'--color-text-maxcontrast' => $this->util->lighten($colorMainText, 33),
|
||||
'--color-text-light' => $colorMainText,
|
||||
'--color-text-lighter' => $this->util->lighten($colorMainText, 33),
|
||||
|
||||
// info/warning/success feedback colours
|
||||
'--color-error' => '#e9322d',
|
||||
'--color-error-hover' => $this->util->mix('#e9322d', $colorMainBackground, 80),
|
||||
'--color-warning' => '#eca700',
|
||||
'--color-warning-hover' => $this->util->mix('#eca700', $colorMainBackground, 80),
|
||||
'--color-success' => '#46ba61',
|
||||
'--color-success-hover' => $this->util->mix('#46ba61', $colorMainBackground, 80),
|
||||
|
||||
// used for the icon loading animation
|
||||
'--color-loading-light' => '#cccccc',
|
||||
'--color-loading-dark' => '#444444',
|
||||
|
||||
'--color-box-shadow-rgb' => $colorBoxShadowRGB,
|
||||
'--color-box-shadow' => "rgba(var(--color-box-shadow-rgb), 0.5)",
|
||||
|
||||
'--color-border' => $this->util->darken($colorMainBackground, 7),
|
||||
'--color-border-dark' => $this->util->darken($colorMainBackground, 14),
|
||||
|
||||
'--font-face' => "-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen-Sans, Cantarell, Ubuntu, 'Helvetica Neue', Arial, sans-serif, 'Noto Color Emoji', 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'",
|
||||
'--default-font-size' => '15px',
|
||||
|
||||
// TODO: support "(prefers-reduced-motion)"
|
||||
'--animation-quick' => '100ms',
|
||||
'--animation-slow' => '300ms',
|
||||
|
||||
// Default variables --------------------------------------------
|
||||
'--image-logo' => "url('$logoSvgPath')",
|
||||
'--image-login' => "url('$backgroundSvgPath')",
|
||||
'--image-logoheader' => "url('$logoSvgPath')",
|
||||
'--image-favicon' => "url('$logoSvgPath')",
|
||||
|
||||
'--border-radius' => '3px',
|
||||
'--border-radius-large' => '10px',
|
||||
// pill-style button, value is large so big buttons also have correct roundness
|
||||
'--border-radius-pill' => '100px',
|
||||
|
||||
'--default-line-height' => '24px',
|
||||
|
||||
// various structure data
|
||||
'--header-height' => '50px',
|
||||
'--navigation-width' => '300px',
|
||||
'--sidebar-min-width' => '300px',
|
||||
'--sidebar-max-width' => '500px',
|
||||
'--list-min-width' => '200px',
|
||||
'--list-max-width' => '300px',
|
||||
'--header-menu-item-height' => '44px',
|
||||
'--header-menu-profile-item-height' => '66px',
|
||||
|
||||
// mobile. Keep in sync with core/js/js.js
|
||||
'--breakpoint-mobile' => '1024px',
|
||||
|
||||
// invert filter if primary is too bright
|
||||
// to be used for legacy reasons only. Use inline
|
||||
// svg with proper css variable instead or material
|
||||
// design icons.
|
||||
'--primary-invert-if-bright' => $this->util->invertTextColor($this->primaryColor) ? 'invert(100%)' : 'unset',
|
||||
'--background-invert-if-bright' => 'unset',
|
||||
];
|
||||
}
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
<?php
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* @copyright Copyright (c) 2022 Joas Schilling <coding@schilljs.com>
|
||||
*
|
||||
* @author Joas Schilling <coding@schilljs.com>
|
||||
* @author John Molakvoæ <skjnldsv@protonmail.com>
|
||||
*
|
||||
* @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/>.
|
||||
*
|
||||
*/
|
||||
namespace OCA\Theming\Themes;
|
||||
|
||||
use OCA\Theming\ITheme;
|
||||
|
||||
class HighContrastTheme extends DefaultTheme implements ITheme {
|
||||
|
||||
public function getId(): string {
|
||||
return 'highcontrast';
|
||||
}
|
||||
|
||||
public function getMediaQuery(): string {
|
||||
return '(prefers-contrast: more)';
|
||||
}
|
||||
|
||||
public function getCSSVariables(): array {
|
||||
$variables = parent::getCSSVariables();
|
||||
|
||||
// FIXME …
|
||||
$variables = $variables;
|
||||
|
||||
return $variables;
|
||||
}
|
||||
}
|
|
@ -34,17 +34,13 @@ use OCP\Files\IAppData;
|
|||
use OCP\Files\NotFoundException;
|
||||
use OCP\Files\SimpleFS\ISimpleFile;
|
||||
use OCP\IConfig;
|
||||
use Mexitek\PHPColors\Color;
|
||||
|
||||
class Util {
|
||||
|
||||
/** @var IConfig */
|
||||
private $config;
|
||||
|
||||
/** @var IAppManager */
|
||||
private $appManager;
|
||||
|
||||
/** @var IAppData */
|
||||
private $appData;
|
||||
private IConfig $config;
|
||||
private IAppManager $appManager;
|
||||
private IAppData $appData;
|
||||
|
||||
/**
|
||||
* Util constructor.
|
||||
|
@ -95,6 +91,21 @@ class Util {
|
|||
return $color;
|
||||
}
|
||||
|
||||
public function mix(string $color1, string $color2, int $factor): string {
|
||||
$color = new Color($color1);
|
||||
return '#' . $color->mix($color2, $factor);
|
||||
}
|
||||
|
||||
public function lighten(string $color, int $factor): string {
|
||||
$color = new Color($color);
|
||||
return '#' . $color->lighten($factor);
|
||||
}
|
||||
|
||||
public function darken(string $color, int $factor): string {
|
||||
$color = new Color($color);
|
||||
return '#' . $color->darken($factor);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert RGB to HSL
|
||||
*
|
||||
|
@ -106,38 +117,16 @@ class Util {
|
|||
*
|
||||
* @return array
|
||||
*/
|
||||
public function toHSL($red, $green, $blue) {
|
||||
$min = min($red, $green, $blue);
|
||||
$max = max($red, $green, $blue);
|
||||
$l = $min + $max;
|
||||
$d = $max - $min;
|
||||
|
||||
if ((int) $d === 0) {
|
||||
$h = $s = 0;
|
||||
} else {
|
||||
if ($l < 255) {
|
||||
$s = $d / $l;
|
||||
} else {
|
||||
$s = $d / (510 - $l);
|
||||
}
|
||||
|
||||
if ($red == $max) {
|
||||
$h = 60 * ($green - $blue) / $d;
|
||||
} elseif ($green == $max) {
|
||||
$h = 60 * ($blue - $red) / $d + 120;
|
||||
} else {
|
||||
$h = 60 * ($red - $green) / $d + 240;
|
||||
}
|
||||
}
|
||||
|
||||
return [fmod($h, 360), $s * 100, $l / 5.1];
|
||||
public function toHSL(string $red, string $green, string $blue): array {
|
||||
$color = new Color(Color::rgbToHex(['R' => $red, 'G' => $green, 'B' => $blue]));
|
||||
return array_values($color->getHsl());
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $color rgb color value
|
||||
* @return float
|
||||
*/
|
||||
public function calculateLuminance($color) {
|
||||
public function calculateLuminance(string $color): float {
|
||||
[$red, $green, $blue] = $this->hexToRGB($color);
|
||||
$hsl = $this->toHSL($red, $green, $blue);
|
||||
return $hsl[2] / 100;
|
||||
|
@ -147,7 +136,7 @@ class Util {
|
|||
* @param string $color rgb color value
|
||||
* @return float
|
||||
*/
|
||||
public function calculateLuma($color) {
|
||||
public function calculateLuma(string $color): float {
|
||||
[$red, $green, $blue] = $this->hexToRGB($color);
|
||||
return (0.2126 * $red + 0.7152 * $green + 0.0722 * $blue) / 255;
|
||||
}
|
||||
|
@ -157,19 +146,9 @@ class Util {
|
|||
* @return int[]
|
||||
* @psalm-return array{0: int, 1: int, 2: int}
|
||||
*/
|
||||
public function hexToRGB($color) {
|
||||
$hex = preg_replace("/[^0-9A-Fa-f]/", '', $color);
|
||||
if (strlen($hex) === 3) {
|
||||
$hex = $hex[0] . $hex[0] . $hex[1] . $hex[1] . $hex[2] . $hex[2];
|
||||
}
|
||||
if (strlen($hex) !== 6) {
|
||||
return [0, 0, 0];
|
||||
}
|
||||
return [
|
||||
hexdec(substr($hex, 0, 2)),
|
||||
hexdec(substr($hex, 2, 2)),
|
||||
hexdec(substr($hex, 4, 2))
|
||||
];
|
||||
public function hexToRGB(string $color): array {
|
||||
$color = new Color($color);
|
||||
return array_values($color->getRgb());
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -25,7 +25,8 @@
|
|||
"ext-pdo": "*",
|
||||
"ext-simplexml": "*",
|
||||
"ext-xmlreader": "*",
|
||||
"ext-zip": "*"
|
||||
"ext-zip": "*",
|
||||
"mexitek/phpcolors": "^1.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"bamarni/composer-bin-plugin": "^1.4"
|
||||
|
|
|
@ -4,8 +4,62 @@
|
|||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "8333c8a239fe5ccec285dfbccc17cca4",
|
||||
"packages": [],
|
||||
"content-hash": "8f8b099365b3839a80b19e266c4ba5e4",
|
||||
"packages": [
|
||||
{
|
||||
"name": "mexitek/phpcolors",
|
||||
"version": "v1.0.4",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/mexitek/phpColors.git",
|
||||
"reference": "4043974240ca7dc3c2bec3c158588148b605b206"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/mexitek/phpColors/zipball/4043974240ca7dc3c2bec3c158588148b605b206",
|
||||
"reference": "4043974240ca7dc3c2bec3c158588148b605b206",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": "^7.2|^8.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"nette/tester": "^2.3",
|
||||
"squizlabs/php_codesniffer": "^3.5"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"classmap": [
|
||||
"src"
|
||||
]
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Arlo Carreon",
|
||||
"homepage": "http://arlocarreon.com",
|
||||
"role": "creator"
|
||||
}
|
||||
],
|
||||
"description": "A series of methods that let you manipulate colors. Just incase you ever need different shades of one color on the fly.",
|
||||
"homepage": "http://mexitek.github.com/phpColors/",
|
||||
"keywords": [
|
||||
"color",
|
||||
"css",
|
||||
"design",
|
||||
"frontend",
|
||||
"ui"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/mexitek/phpColors/issues",
|
||||
"source": "https://github.com/mexitek/phpColors"
|
||||
},
|
||||
"time": "2021-11-26T13:19:08+00:00"
|
||||
}
|
||||
],
|
||||
"packages-dev": [
|
||||
{
|
||||
"name": "bamarni/composer-bin-plugin",
|
||||
|
|
|
@ -285,6 +285,8 @@ kbd {
|
|||
margin-right: 11px;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
// Legacy invert if bright background
|
||||
filter: var(--background-invert-if-bright);
|
||||
}
|
||||
|
||||
/* counter can also be inside the link */
|
||||
|
|
|
@ -449,6 +449,11 @@ nav[role='navigation'] {
|
|||
// Make sure most app names don’t ellipsize
|
||||
letter-spacing: -0.5px;
|
||||
font-size: 12px;
|
||||
|
||||
// If the primary is too bright, invert the app icons
|
||||
svg image {
|
||||
filter: var(--primary-invert-if-bright);
|
||||
}
|
||||
}
|
||||
|
||||
/* focused app visual feedback */
|
||||
|
|
|
@ -975,6 +975,7 @@ span.ui-icon {
|
|||
background-size: 20px 20px;
|
||||
padding: 14px;
|
||||
cursor: pointer;
|
||||
filter: var(--primary-invert-if-bright);
|
||||
|
||||
&:hover,
|
||||
&:focus,
|
||||
|
|
|
@ -64,7 +64,7 @@ $getUserAvatar = static function (int $size) use ($_): string {
|
|||
</div>
|
||||
</a>
|
||||
|
||||
<ul id="appmenu" <?php if ($_['themingInvertMenu']) { ?>class="inverted"<?php } ?>>
|
||||
<ul id="appmenu">
|
||||
<?php foreach ($_['navigation'] as $entry): ?>
|
||||
<li data-id="<?php p($entry['id']); ?>" class="hidden" tabindex="-1">
|
||||
<a href="<?php print_unescaped($entry['href']); ?>"
|
||||
|
@ -73,13 +73,12 @@ $getUserAvatar = static function (int $size) use ($_): string {
|
|||
aria-label="<?php p($entry['name']); ?>">
|
||||
<svg width="24" height="20" viewBox="0 0 24 20" alt=""<?php if ($entry['unread'] !== 0) { ?> class="has-unread"<?php } ?>>
|
||||
<defs>
|
||||
<?php if ($_['themingInvertMenu']) { ?><filter id="invertMenuMain-<?php p($entry['id']); ?>"><feColorMatrix in="SourceGraphic" type="matrix" values="-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 0 0 0 1 0" /></filter><?php } ?>
|
||||
<mask id="hole">
|
||||
<rect width="100%" height="100%" fill="white"/>
|
||||
<circle r="4.5" cx="21" cy="3" fill="black"/>
|
||||
</mask>
|
||||
</defs>
|
||||
<image x="2" y="0" width="20" height="20" preserveAspectRatio="xMinYMin meet"<?php if ($_['themingInvertMenu']) { ?> filter="url(#invertMenuMain-<?php p($entry['id']); ?>)"<?php } ?> xlink:href="<?php print_unescaped($entry['icon'] . '?v=' . $_['versionHash']); ?>" style="<?php if ($entry['unread'] !== 0) { ?>mask: url("#hole");<?php } ?>" class="app-icon"></image>
|
||||
<image x="2" y="0" width="20" height="20" preserveAspectRatio="xMinYMin meet" xlink:href="<?php print_unescaped($entry['icon'] . '?v=' . $_['versionHash']); ?>" style="<?php if ($entry['unread'] !== 0) { ?>mask: url("#hole");<?php } ?>" class="app-icon"></image>
|
||||
<circle class="app-icon-notification" r="3" cx="21" cy="3" fill="red"/>
|
||||
</svg>
|
||||
<div class="unread-counter" aria-hidden="true"><?php p($entry['unread']); ?></div>
|
||||
|
|
|
@ -141,17 +141,6 @@ class TemplateLayout extends \OC_Template {
|
|||
$this->assign('userAvatarSet', true);
|
||||
$this->assign('userAvatarVersion', $this->config->getUserValue(\OC_User::getUser(), 'avatar', 'version', 0));
|
||||
}
|
||||
|
||||
// check if app menu icons should be inverted
|
||||
try {
|
||||
/** @var \OCA\Theming\Util $util */
|
||||
$util = \OC::$server->query(\OCA\Theming\Util::class);
|
||||
$this->assign('themingInvertMenu', $util->invertTextColor(\OC::$server->getThemingDefaults()->getColorPrimary()));
|
||||
} catch (\OCP\AppFramework\QueryException $e) {
|
||||
$this->assign('themingInvertMenu', false);
|
||||
} catch (\OCP\AutoloadNotAllowedException $e) {
|
||||
$this->assign('themingInvertMenu', false);
|
||||
}
|
||||
} elseif ($renderAs === TemplateResponse::RENDER_AS_ERROR) {
|
||||
parent::__construct('core', 'layout.guest', '', false);
|
||||
$this->assign('bodyid', 'body-login');
|
||||
|
|
|
@ -105,7 +105,7 @@ class OC_Template extends \OC\Template\Base {
|
|||
// apps that started before the template initialization can load their own scripts/styles
|
||||
// so to make sure this scripts/styles here are loaded first we put all core scripts first
|
||||
// check lib/public/Util.php
|
||||
OC_Util::addStyle('css-variables', null, true);
|
||||
// OC_Util::addStyle('css-variables', null, true);
|
||||
OC_Util::addStyle('server', null, true);
|
||||
|
||||
// include common nextcloud webpack bundle
|
||||
|
|
Loading…
Reference in New Issue