setup
This commit is contained in:
@@ -8,41 +8,6 @@ $modules = array_values(array_filter(
|
|||||||
static fn (array $module): bool => !empty($module['enabled'])
|
static fn (array $module): bool => !empty($module['enabled'])
|
||||||
));
|
));
|
||||||
?>
|
?>
|
||||||
<section class="home-hero" data-reveal>
|
|
||||||
<a class="brand-mark" href="/" aria-label="Nexus">
|
|
||||||
<img src="/assets/images/logo.png" alt="Nexus Logo">
|
|
||||||
</a>
|
|
||||||
<div class="brand-copy">
|
|
||||||
<span class="eyebrow">Nexus</span>
|
|
||||||
<h1><?= e(defined('APP_DOMAIN_PRIMARY') ? (string)APP_DOMAIN_PRIMARY : 'Nexus') ?></h1>
|
|
||||||
<p>Kompakter Einstieg fuer die verfuegbaren Module.</p>
|
|
||||||
</div>
|
|
||||||
<div class="theme-switcher" aria-label="Farbschema">
|
|
||||||
<?php if ($auth->isEnabled()): ?>
|
|
||||||
<a class="auth-pill" href="<?= $authUser === null ? '/auth/login' : '/auth/logout' ?>">
|
|
||||||
<?= $authUser === null ? 'Login' : 'Logout ' . e((string)($authUser['username'] ?? $authUser['name'] ?? '')) ?>
|
|
||||||
</a>
|
|
||||||
<?php endif; ?>
|
|
||||||
<label>
|
|
||||||
<span>Modus</span>
|
|
||||||
<select data-theme-mode>
|
|
||||||
<option value="day">Day</option>
|
|
||||||
<option value="night">Night</option>
|
|
||||||
</select>
|
|
||||||
</label>
|
|
||||||
<label>
|
|
||||||
<span>Farbe</span>
|
|
||||||
<select data-theme-accent>
|
|
||||||
<option value="logo">Logo</option>
|
|
||||||
<option value="pink">Pink</option>
|
|
||||||
<option value="cyan">Cyan</option>
|
|
||||||
<option value="orange">Orange</option>
|
|
||||||
<option value="green">Gruen</option>
|
|
||||||
</select>
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<section class="module-list-section" data-reveal>
|
<section class="module-list-section" data-reveal>
|
||||||
<div class="section-head">
|
<div class="section-head">
|
||||||
<div>
|
<div>
|
||||||
|
|||||||
@@ -60,6 +60,8 @@ $getNested = function (array $source, string $path): mixed {
|
|||||||
};
|
};
|
||||||
|
|
||||||
$dbGroups = [];
|
$dbGroups = [];
|
||||||
|
$fieldsByDbGroup = [];
|
||||||
|
$generalFields = [];
|
||||||
foreach ($fields as $field) {
|
foreach ($fields as $field) {
|
||||||
$name = (string)($field['name'] ?? '');
|
$name = (string)($field['name'] ?? '');
|
||||||
if (!str_contains($name, '.')) {
|
if (!str_contains($name, '.')) {
|
||||||
@@ -77,6 +79,68 @@ foreach ($fields as $field) {
|
|||||||
$dbGroups[$group] = $label;
|
$dbGroups[$group] = $label;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
foreach ($fields as $field) {
|
||||||
|
$name = (string)($field['name'] ?? '');
|
||||||
|
if (str_contains($name, '.')) {
|
||||||
|
[$group] = explode('.', $name, 2);
|
||||||
|
if (array_key_exists($group, $dbGroups)) {
|
||||||
|
$fieldsByDbGroup[$group][] = $field;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$generalFields[] = $field;
|
||||||
|
}
|
||||||
|
|
||||||
|
$driverOptions = [
|
||||||
|
'pgsql' => 'PostgreSQL',
|
||||||
|
'mysql' => 'MySQL / MariaDB',
|
||||||
|
'sqlite' => 'SQLite',
|
||||||
|
];
|
||||||
|
|
||||||
|
$renderField = function (array $field) use ($current, $getNested, $driverOptions): void {
|
||||||
|
$name = (string)($field['name'] ?? '');
|
||||||
|
if ($name === '') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$label = (string)($field['label'] ?? $name);
|
||||||
|
$type = (string)($field['type'] ?? 'text');
|
||||||
|
$required = !empty($field['required']);
|
||||||
|
$help = (string)($field['help'] ?? $field['description'] ?? '');
|
||||||
|
$postKey = str_replace('.', '_', $name);
|
||||||
|
|
||||||
|
$value = '';
|
||||||
|
if ($name === 'kea_auto_init') {
|
||||||
|
$value = !empty($current[$name]) ? '1' : '0';
|
||||||
|
} elseif (str_contains($name, '.')) {
|
||||||
|
$value = (string)($getNested($current, $name) ?? '');
|
||||||
|
} else {
|
||||||
|
$value = (string)($current[$name] ?? '');
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
<label class="setup-field muted">
|
||||||
|
<span><?= e($label) ?></span>
|
||||||
|
<?php if (str_ends_with($name, '.driver')): ?>
|
||||||
|
<select name="<?= e($postKey) ?>" <?= $required ? 'required' : '' ?>>
|
||||||
|
<option value="">Bitte waehlen</option>
|
||||||
|
<?php foreach ($driverOptions as $driver => $driverLabel): ?>
|
||||||
|
<option value="<?= e($driver) ?>" <?= $value === $driver ? 'selected' : '' ?>><?= e($driverLabel) ?></option>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</select>
|
||||||
|
<?php elseif ($type === 'textarea'): ?>
|
||||||
|
<textarea name="<?= e($postKey) ?>" rows="3" <?= $required ? 'required' : '' ?>><?= e($value) ?></textarea>
|
||||||
|
<?php elseif ($type === 'checkbox'): ?>
|
||||||
|
<input type="checkbox" name="<?= e($postKey) ?>" value="1" <?= $value === '1' ? 'checked' : '' ?>>
|
||||||
|
<?php else: ?>
|
||||||
|
<input type="<?= e($type) ?>" name="<?= e($postKey) ?>" value="<?= e($value) ?>" <?= $required ? 'required' : '' ?>>
|
||||||
|
<?php endif; ?>
|
||||||
|
<?php if ($help !== ''): ?>
|
||||||
|
<small class="muted"><?= e($help) ?></small>
|
||||||
|
<?php endif; ?>
|
||||||
|
</label>
|
||||||
|
<?php
|
||||||
|
};
|
||||||
|
|
||||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||||
$payload = [];
|
$payload = [];
|
||||||
|
|
||||||
@@ -142,9 +206,9 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
?>
|
?>
|
||||||
<div class="card">
|
<div class="setup-shell">
|
||||||
<div class="pill">Setup</div>
|
<div class="pill">Setup</div>
|
||||||
<h1 style="margin-top:.75rem;"><?= e($module['title']) ?> – Einrichtung</h1>
|
<h1 class="setup-title"><?= e($module['title']) ?> – Einrichtung</h1>
|
||||||
<p class="muted">Trage die benötigten Informationen für das Modul ein.</p>
|
<p class="muted">Trage die benötigten Informationen für das Modul ein.</p>
|
||||||
|
|
||||||
<?php if ($error): ?>
|
<?php if ($error): ?>
|
||||||
@@ -152,60 +216,66 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
|||||||
<?= e($error) ?>
|
<?= e($error) ?>
|
||||||
</div>
|
</div>
|
||||||
<?php elseif ($notice): ?>
|
<?php elseif ($notice): ?>
|
||||||
<div class="card" style="margin-top:1rem; border-color:var(--accent-2);">
|
<div class="setup-notice">
|
||||||
<?= e($notice) ?>
|
<?= e($notice) ?>
|
||||||
</div>
|
</div>
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
|
|
||||||
<form method="post" style="margin-top:1rem; display:grid; gap:14px; max-width:520px;">
|
<form method="post" class="setup-form">
|
||||||
<?php foreach ($fields as $field): ?>
|
<?php if ($generalFields !== []): ?>
|
||||||
<?php
|
<section class="setup-panel">
|
||||||
$name = (string)($field['name'] ?? '');
|
<div class="setup-panel__head">
|
||||||
$label = (string)($field['label'] ?? $name);
|
<div>
|
||||||
$type = (string)($field['type'] ?? 'text');
|
<span class="pill">Allgemein</span>
|
||||||
$required = !empty($field['required']);
|
<h2>Moduleinstellungen</h2>
|
||||||
$help = (string)($field['help'] ?? $field['description'] ?? '');
|
</div>
|
||||||
$postKey = str_replace('.', '_', $name);
|
</div>
|
||||||
|
<div class="setup-grid">
|
||||||
$value = '';
|
<?php foreach ($generalFields as $field): ?>
|
||||||
if ($name === 'kea_auto_init') {
|
<?php $renderField($field); ?>
|
||||||
$value = !empty($current[$name]) ? '1' : '0';
|
|
||||||
} elseif (str_contains($name, '.')) {
|
|
||||||
$value = (string)($getNested($current, $name) ?? '');
|
|
||||||
} else {
|
|
||||||
$value = (string)($current[$name] ?? '');
|
|
||||||
}
|
|
||||||
?>
|
|
||||||
<label class="muted" style="display:grid; gap:6px;">
|
|
||||||
<span><?= e($label) ?></span>
|
|
||||||
<?php if ($type === 'textarea'): ?>
|
|
||||||
<textarea name="<?= e($postKey) ?>" rows="3" <?= $required ? 'required' : '' ?>><?= e($value) ?></textarea>
|
|
||||||
<?php elseif ($type === 'checkbox'): ?>
|
|
||||||
<input type="checkbox" name="<?= e($postKey) ?>" value="1" <?= $value === '1' ? 'checked' : '' ?>>
|
|
||||||
<?php else: ?>
|
|
||||||
<input type="<?= e($type) ?>" name="<?= e($postKey) ?>" value="<?= e($value) ?>" <?= $required ? 'required' : '' ?>>
|
|
||||||
<?php endif; ?>
|
|
||||||
<?php if ($help !== ''): ?>
|
|
||||||
<small class="muted"><?= e($help) ?></small>
|
|
||||||
<?php endif; ?>
|
|
||||||
</label>
|
|
||||||
<?php endforeach; ?>
|
<?php endforeach; ?>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
<?php endif; ?>
|
||||||
|
|
||||||
<?php if ($dbGroups !== []): ?>
|
<?php if ($dbGroups !== []): ?>
|
||||||
<div class="card" style="padding:14px; background:var(--panel-2); display:grid; gap:10px;">
|
<section class="setup-panel setup-panel--flat">
|
||||||
<strong>Datenbankverbindungen testen</strong>
|
<div class="setup-panel__head">
|
||||||
<small class="muted">Der Test nutzt die aktuell eingetragenen Werte aus diesem Formular und speichert sie nicht.</small>
|
<div>
|
||||||
<div style="display:flex; gap:10px; flex-wrap:wrap;">
|
<span class="pill">Datenbanken</span>
|
||||||
|
<h2>Verbindungen</h2>
|
||||||
|
<p class="muted">Jede Verbindung kann getrennt konfiguriert und getestet werden.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="setup-tabs" aria-label="Datenbankbereiche">
|
||||||
<?php foreach ($dbGroups as $group => $label): ?>
|
<?php foreach ($dbGroups as $group => $label): ?>
|
||||||
<button class="nav-link" type="submit" name="test_db" value="<?= e($group) ?>" formnovalidate>
|
<a class="nav-link" href="#setup-db-<?= e($group) ?>"><?= e($label) ?></a>
|
||||||
<?= e($label) ?> testen
|
|
||||||
</button>
|
|
||||||
<?php endforeach; ?>
|
<?php endforeach; ?>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="setup-db-panels">
|
||||||
|
<?php foreach ($dbGroups as $group => $label): ?>
|
||||||
|
<section class="setup-db-panel" id="setup-db-<?= e($group) ?>">
|
||||||
|
<div class="setup-panel__head">
|
||||||
|
<div>
|
||||||
|
<span class="pill"><?= e($label) ?></span>
|
||||||
|
<h3><?= e($label) ?> konfigurieren</h3>
|
||||||
</div>
|
</div>
|
||||||
|
<button class="nav-link" type="submit" name="test_db" value="<?= e($group) ?>" formnovalidate>
|
||||||
|
Verbindung testen
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div class="setup-grid">
|
||||||
|
<?php foreach (($fieldsByDbGroup[$group] ?? []) as $field): ?>
|
||||||
|
<?php $renderField($field); ?>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
|
|
||||||
<div style="display:flex; gap:10px;">
|
<div class="setup-actions">
|
||||||
<button class="cta-button" type="submit">Speichern</button>
|
<button class="cta-button" type="submit">Speichern</button>
|
||||||
<a class="nav-link" href="/modules/access/<?= e($moduleName) ?>">Zugriff verwalten</a>
|
<a class="nav-link" href="/modules/access/<?= e($moduleName) ?>">Zugriff verwalten</a>
|
||||||
<a class="nav-link" href="/modules">Zurück</a>
|
<a class="nav-link" href="/modules">Zurück</a>
|
||||||
|
|||||||
@@ -1,3 +1,16 @@
|
|||||||
|
<?php
|
||||||
|
$requestPath = app()->request()->path();
|
||||||
|
$currentModuleName = current_module_name();
|
||||||
|
if ($currentModuleName === null && preg_match('~^/modules/(?:setup|access)/([a-zA-Z0-9_-]+)~', $requestPath, $moduleMatch)) {
|
||||||
|
$currentModuleName = $moduleMatch[1];
|
||||||
|
}
|
||||||
|
$currentModule = $currentModuleName !== null ? modules()->get($currentModuleName) : null;
|
||||||
|
$headerEyebrow = $currentModule ? 'Modul' : 'Nexus';
|
||||||
|
$headerTitle = $currentModule ? (string)($currentModule['title'] ?? $currentModuleName) : (defined('APP_DOMAIN_PRIMARY') ? (string)APP_DOMAIN_PRIMARY : 'Nexus');
|
||||||
|
$headerText = $currentModule ? (string)($currentModule['description'] ?? '') : 'Kompakter Einstieg fuer die verfuegbaren Module.';
|
||||||
|
$auth = app()->auth();
|
||||||
|
$authUser = $auth->user();
|
||||||
|
?>
|
||||||
<!doctype html>
|
<!doctype html>
|
||||||
<html lang="de">
|
<html lang="de">
|
||||||
<head>
|
<head>
|
||||||
@@ -6,18 +19,62 @@
|
|||||||
<title>Nexus</title>
|
<title>Nexus</title>
|
||||||
<script>
|
<script>
|
||||||
(() => {
|
(() => {
|
||||||
try {
|
const moduleName = <?= json_encode($currentModuleName) ?>;
|
||||||
const theme = localStorage.getItem('nexus.theme') || 'day';
|
const read = (key, fallback) => {
|
||||||
const accent = localStorage.getItem('nexus.accent') || 'logo';
|
try { return localStorage.getItem(key) || fallback; } catch (error) { return fallback; }
|
||||||
document.documentElement.dataset.theme = theme;
|
};
|
||||||
document.documentElement.dataset.accent = accent;
|
const mainTheme = read('nexus.theme', 'day');
|
||||||
} catch (error) {
|
const mainAccent = read('nexus.accent', 'logo');
|
||||||
document.documentElement.dataset.theme = 'day';
|
const moduleTheme = moduleName ? read(`nexus.module.${moduleName}.theme`, 'inherit') : mainTheme;
|
||||||
document.documentElement.dataset.accent = 'logo';
|
const moduleAccent = moduleName ? read(`nexus.module.${moduleName}.accent`, 'inherit') : mainAccent;
|
||||||
}
|
document.documentElement.dataset.module = moduleName || '';
|
||||||
|
document.documentElement.dataset.theme = moduleTheme === 'inherit' ? mainTheme : moduleTheme;
|
||||||
|
document.documentElement.dataset.accent = moduleAccent === 'inherit' ? mainAccent : moduleAccent;
|
||||||
})();
|
})();
|
||||||
</script>
|
</script>
|
||||||
<link rel="stylesheet" href="<?= e(app()->assets()->versioned('/assets/css/app.css')) ?>">
|
<link rel="stylesheet" href="<?= e(app()->assets()->versioned('/assets/css/app.css')) ?>">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<main class="main-shell">
|
<main class="main-shell">
|
||||||
|
<section class="home-hero app-header" data-module-name="<?= e((string)($currentModuleName ?? '')) ?>">
|
||||||
|
<a class="brand-mark" href="/" aria-label="Nexus">
|
||||||
|
<img src="/assets/images/logo.png" alt="Nexus Logo">
|
||||||
|
</a>
|
||||||
|
<div class="brand-copy">
|
||||||
|
<span class="eyebrow"><?= e($headerEyebrow) ?></span>
|
||||||
|
<h1><?= e($headerTitle) ?></h1>
|
||||||
|
<?php if ($headerText !== ''): ?>
|
||||||
|
<p><?= e($headerText) ?></p>
|
||||||
|
<?php endif; ?>
|
||||||
|
</div>
|
||||||
|
<div class="theme-switcher" aria-label="Darstellung">
|
||||||
|
<?php if ($auth->isEnabled()): ?>
|
||||||
|
<a class="auth-pill" href="<?= $authUser === null ? '/auth/login' : '/auth/logout' ?>">
|
||||||
|
<?= $authUser === null ? 'Login' : 'Logout ' . e((string)($authUser['username'] ?? $authUser['name'] ?? '')) ?>
|
||||||
|
</a>
|
||||||
|
<?php endif; ?>
|
||||||
|
<label>
|
||||||
|
<span>Modus</span>
|
||||||
|
<select data-theme-mode data-theme-scope="<?= $currentModuleName !== null ? 'module' : 'main' ?>">
|
||||||
|
<?php if ($currentModuleName !== null): ?>
|
||||||
|
<option value="inherit">Wie Hauptsystem</option>
|
||||||
|
<?php endif; ?>
|
||||||
|
<option value="day">Day</option>
|
||||||
|
<option value="night">Night</option>
|
||||||
|
</select>
|
||||||
|
</label>
|
||||||
|
<label>
|
||||||
|
<span>Farbe</span>
|
||||||
|
<select data-theme-accent data-theme-scope="<?= $currentModuleName !== null ? 'module' : 'main' ?>">
|
||||||
|
<?php if ($currentModuleName !== null): ?>
|
||||||
|
<option value="inherit">Wie Hauptsystem</option>
|
||||||
|
<?php endif; ?>
|
||||||
|
<option value="logo">Logo</option>
|
||||||
|
<option value="pink">Pink</option>
|
||||||
|
<option value="cyan">Cyan</option>
|
||||||
|
<option value="orange">Orange</option>
|
||||||
|
<option value="green">Gruen</option>
|
||||||
|
</select>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|||||||
@@ -194,6 +194,10 @@ a {
|
|||||||
color: var(--muted);
|
color: var(--muted);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.app-header {
|
||||||
|
margin-bottom: 18px;
|
||||||
|
}
|
||||||
|
|
||||||
.stats {
|
.stats {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
|
grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
|
||||||
@@ -591,3 +595,98 @@ a {
|
|||||||
grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
|
grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
|
||||||
gap: 14px;
|
gap: 14px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.setup-shell {
|
||||||
|
display: grid;
|
||||||
|
gap: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.setup-title {
|
||||||
|
margin: 0.75rem 0 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.setup-notice {
|
||||||
|
margin-top: 1rem;
|
||||||
|
border: 1px solid var(--accent-2);
|
||||||
|
border-radius: 8px;
|
||||||
|
background: color-mix(in srgb, var(--surface) 94%, transparent);
|
||||||
|
padding: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.setup-form {
|
||||||
|
margin-top: 1rem;
|
||||||
|
display: grid;
|
||||||
|
gap: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.setup-panel,
|
||||||
|
.setup-db-panel {
|
||||||
|
border: 1px solid var(--line);
|
||||||
|
border-radius: 8px;
|
||||||
|
background: color-mix(in srgb, var(--surface) 92%, transparent);
|
||||||
|
padding: 16px;
|
||||||
|
box-shadow: 0 10px 24px rgba(1, 22, 32, 0.06);
|
||||||
|
}
|
||||||
|
|
||||||
|
.setup-panel--flat {
|
||||||
|
border: 0;
|
||||||
|
border-radius: 0;
|
||||||
|
background: transparent;
|
||||||
|
padding: 0;
|
||||||
|
box-shadow: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.setup-db-panel {
|
||||||
|
scroll-margin-top: 18px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.setup-panel__head {
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-start;
|
||||||
|
justify-content: space-between;
|
||||||
|
gap: 14px;
|
||||||
|
margin-bottom: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.setup-panel__head h2,
|
||||||
|
.setup-panel__head h3 {
|
||||||
|
margin: 8px 0 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.setup-panel__head p {
|
||||||
|
margin: 6px 0 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.setup-tabs {
|
||||||
|
display: flex;
|
||||||
|
gap: 10px;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
margin-bottom: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.setup-db-panels {
|
||||||
|
display: grid;
|
||||||
|
gap: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.setup-grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
|
||||||
|
gap: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.setup-field {
|
||||||
|
display: grid;
|
||||||
|
gap: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.setup-field > span {
|
||||||
|
font-weight: 800;
|
||||||
|
color: var(--text);
|
||||||
|
}
|
||||||
|
|
||||||
|
.setup-actions {
|
||||||
|
display: flex;
|
||||||
|
gap: 10px;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
|||||||
@@ -8,38 +8,75 @@ function readThemePreference(key, fallback) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const themeMode = readThemePreference('nexus.theme', document.documentElement.dataset.theme || 'day');
|
const moduleName = document.documentElement.dataset.module || '';
|
||||||
const themeAccent = readThemePreference('nexus.accent', document.documentElement.dataset.accent || 'logo');
|
const mainThemeMode = readThemePreference('nexus.theme', 'day');
|
||||||
|
const mainThemeAccent = readThemePreference('nexus.accent', 'logo');
|
||||||
|
const moduleThemeMode = moduleName ? readThemePreference(`nexus.module.${moduleName}.theme`, 'inherit') : mainThemeMode;
|
||||||
|
const moduleThemeAccent = moduleName ? readThemePreference(`nexus.module.${moduleName}.accent`, 'inherit') : mainThemeAccent;
|
||||||
|
|
||||||
function applyTheme(mode, accent) {
|
function normalizeThemeValue(value, allowed, fallback) {
|
||||||
const normalizedMode = ['day', 'night'].includes(mode) ? mode : 'day';
|
return allowed.includes(value) ? value : fallback;
|
||||||
const normalizedAccent = ['logo', 'pink', 'cyan', 'orange', 'green'].includes(accent) ? accent : 'logo';
|
}
|
||||||
document.documentElement.dataset.theme = normalizedMode;
|
|
||||||
document.documentElement.dataset.accent = normalizedAccent;
|
function storedTheme() {
|
||||||
|
const rawMode = moduleName ? readThemePreference(`nexus.module.${moduleName}.theme`, 'inherit') : readThemePreference('nexus.theme', 'day');
|
||||||
|
const rawAccent = moduleName ? readThemePreference(`nexus.module.${moduleName}.accent`, 'inherit') : readThemePreference('nexus.accent', 'logo');
|
||||||
|
const mode = moduleName ? normalizeThemeValue(rawMode, ['inherit', 'day', 'night'], 'inherit') : normalizeThemeValue(rawMode, ['day', 'night'], 'day');
|
||||||
|
const accent = moduleName ? normalizeThemeValue(rawAccent, ['inherit', 'logo', 'pink', 'cyan', 'orange', 'green'], 'inherit') : normalizeThemeValue(rawAccent, ['logo', 'pink', 'cyan', 'orange', 'green'], 'logo');
|
||||||
|
return { mode, accent };
|
||||||
|
}
|
||||||
|
|
||||||
|
function applyTheme(mode, accent, persist = true) {
|
||||||
|
const allowedModes = moduleName ? ['inherit', 'day', 'night'] : ['day', 'night'];
|
||||||
|
const allowedAccents = moduleName ? ['inherit', 'logo', 'pink', 'cyan', 'orange', 'green'] : ['logo', 'pink', 'cyan', 'orange', 'green'];
|
||||||
|
const normalizedMode = normalizeThemeValue(mode, allowedModes, moduleName ? 'inherit' : 'day');
|
||||||
|
const normalizedAccent = normalizeThemeValue(accent, allowedAccents, moduleName ? 'inherit' : 'logo');
|
||||||
|
const effectiveMode = normalizedMode === 'inherit' ? mainThemeMode : normalizedMode;
|
||||||
|
const effectiveAccent = normalizedAccent === 'inherit' ? mainThemeAccent : normalizedAccent;
|
||||||
|
document.documentElement.dataset.theme = effectiveMode;
|
||||||
|
document.documentElement.dataset.accent = effectiveAccent;
|
||||||
try {
|
try {
|
||||||
|
if (persist) {
|
||||||
|
if (moduleName) {
|
||||||
|
if (normalizedMode === 'inherit') {
|
||||||
|
localStorage.removeItem(`nexus.module.${moduleName}.theme`);
|
||||||
|
} else {
|
||||||
|
localStorage.setItem(`nexus.module.${moduleName}.theme`, normalizedMode);
|
||||||
|
}
|
||||||
|
if (normalizedAccent === 'inherit') {
|
||||||
|
localStorage.removeItem(`nexus.module.${moduleName}.accent`);
|
||||||
|
} else {
|
||||||
|
localStorage.setItem(`nexus.module.${moduleName}.accent`, normalizedAccent);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
localStorage.setItem('nexus.theme', normalizedMode);
|
localStorage.setItem('nexus.theme', normalizedMode);
|
||||||
localStorage.setItem('nexus.accent', normalizedAccent);
|
localStorage.setItem('nexus.accent', normalizedAccent);
|
||||||
|
}
|
||||||
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// Ignore blocked storage; the current page still receives the theme.
|
// Ignore blocked storage; the current page still receives the theme.
|
||||||
}
|
}
|
||||||
|
return { mode: normalizedMode, accent: normalizedAccent };
|
||||||
}
|
}
|
||||||
|
|
||||||
applyTheme(themeMode, themeAccent);
|
const initialTheme = applyTheme(moduleThemeMode, moduleThemeAccent, false);
|
||||||
|
|
||||||
const themeModeSelect = document.querySelector('[data-theme-mode]');
|
const themeModeSelect = document.querySelector('[data-theme-mode]');
|
||||||
const themeAccentSelect = document.querySelector('[data-theme-accent]');
|
const themeAccentSelect = document.querySelector('[data-theme-accent]');
|
||||||
|
|
||||||
if (themeModeSelect) {
|
if (themeModeSelect) {
|
||||||
themeModeSelect.value = document.documentElement.dataset.theme;
|
themeModeSelect.value = initialTheme.mode;
|
||||||
themeModeSelect.addEventListener('change', () => {
|
themeModeSelect.addEventListener('change', () => {
|
||||||
applyTheme(themeModeSelect.value, document.documentElement.dataset.accent);
|
const current = storedTheme();
|
||||||
|
applyTheme(themeModeSelect.value, current.accent);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (themeAccentSelect) {
|
if (themeAccentSelect) {
|
||||||
themeAccentSelect.value = document.documentElement.dataset.accent;
|
themeAccentSelect.value = initialTheme.accent;
|
||||||
themeAccentSelect.addEventListener('change', () => {
|
themeAccentSelect.addEventListener('change', () => {
|
||||||
applyTheme(document.documentElement.dataset.theme, themeAccentSelect.value);
|
const current = storedTheme();
|
||||||
|
applyTheme(current.mode, themeAccentSelect.value);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user