|
1 | 1 | <?php declare(strict_types=1); |
2 | | - |
3 | 2 | // Copyright (C) 2015-2026 Mark Constable <mc@netserva.org> (MIT License) |
4 | 3 |
|
5 | 4 | namespace SPE\Autoload\Core; |
6 | 5 |
|
7 | | -abstract class Theme |
| 6 | +final class Theme |
8 | 7 | { |
9 | | - public function __construct( |
10 | | - protected Ctx $ctx, |
11 | | - protected array $out, |
12 | | - ) {} |
| 8 | + public function __construct(private Ctx $ctx, private array $out) {} |
| 9 | + |
| 10 | + public function render(): string |
| 11 | + { |
| 12 | + return $this->html($this->topnav() . $this->sidebar('left') . $this->sidebar('right') . "<main>{$this->out['main']}</main>"); |
| 13 | + } |
13 | 14 |
|
14 | | - abstract public function render(): string; |
| 15 | + private function navLinks(): string |
| 16 | + { |
| 17 | + return implode('', array_map(fn($p) => sprintf( |
| 18 | + '<a href="?o=%s"%s data-icon="%s"><i data-lucide="%s"></i> %s</a>', |
| 19 | + $p[2], $this->ctx->in['o'] === $p[2] ? ' class="active"' : '', $p[0], $p[0], $p[1] |
| 20 | + ), $this->ctx->nav)); |
| 21 | + } |
15 | 22 |
|
16 | | - protected function nav(): string |
| 23 | + private function colorLinks(): string |
17 | 24 | { |
18 | | - ['o' => $o, 't' => $t] = $this->ctx->in; |
19 | | - return $this->ctx->nav |
20 | | - |> (static fn($n) => array_map(static fn($p) => sprintf( |
21 | | - '<a href="?o=%s&t=%s"%s><i data-lucide="%s"></i> %s</a>', |
22 | | - $p[2], |
23 | | - $t, |
24 | | - $o === $p[2] ? ' class="active"' : '', |
25 | | - $p[0], |
26 | | - $p[1], |
27 | | - ), $n)) |
28 | | - |> (static fn($a) => implode(' ', $a)); |
| 25 | + return implode('', array_map(fn($p) => sprintf( |
| 26 | + '<a href="#" data-scheme="%s" data-icon="%s"><i data-lucide="%s"></i> %s</a>', $p[2], $p[0], $p[0], $p[1] |
| 27 | + ), $this->ctx->colors)); |
29 | 28 | } |
30 | 29 |
|
31 | | - protected function dropdown(): string |
| 30 | + private function topnav(): string |
32 | 31 | { |
33 | | - ['o' => $o, 't' => $t] = $this->ctx->in; |
34 | | - $links = $this->ctx->themes |
35 | | - |> (static fn($n) => array_map(static fn($p) => sprintf( |
36 | | - '<a href="?o=%s&t=%s"%s><i data-lucide="%s"></i> %s</a>', |
37 | | - $o, |
38 | | - $p[2], |
39 | | - $t === $p[2] ? ' class="active"' : '', |
40 | | - $p[0], |
41 | | - $p[1], |
42 | | - ), $n)) |
43 | | - |> (static fn($a) => implode('', $a)); |
44 | | - return "<div class=\"dropdown\"><span class=\"dropdown-toggle\"><i data-lucide=\"layout-grid\"></i> Layout</span><div class=\"dropdown-menu\">$links</div></div>"; |
| 32 | + return <<<HTML |
| 33 | + <nav class="topnav"> |
| 34 | + <button class="menu-toggle" data-sidebar="left"><i data-lucide="menu"></i></button> |
| 35 | + <h1><a class="brand" href="/"><span>{$this->out['page']}</span></a></h1> |
| 36 | + <button class="menu-toggle" data-sidebar="right"><i data-lucide="menu"></i></button> |
| 37 | + </nav> |
| 38 | + HTML; |
45 | 39 | } |
46 | 40 |
|
47 | | - protected function colors(): string |
| 41 | + private function sidebar(string $side): string |
48 | 42 | { |
| 43 | + [$nav, $title, $icon] = $side === 'left' |
| 44 | + ? [$this->navLinks(), 'Navigation', 'compass'] |
| 45 | + : [$this->colorLinks() . '<div class="sidebar-divider"></div><a href="#" onclick="Base.toggleTheme();return false" data-icon="moon"><i data-lucide="moon"></i> Toggle Theme</a>', 'Settings', 'sliders-horizontal']; |
49 | 46 | return <<<HTML |
50 | | - <div class="dropdown"><span class="dropdown-toggle"><i data-lucide="swatch-book"></i> Colors</span><div class="dropdown-menu"> |
51 | | - <a href="#" data-scheme="default"><i data-lucide="circle"></i> Stone</a> |
52 | | - <a href="#" data-scheme="ocean"><i data-lucide="waves"></i> Ocean</a> |
53 | | - <a href="#" data-scheme="forest"><i data-lucide="trees"></i> Forest</a> |
54 | | - <a href="#" data-scheme="sunset"><i data-lucide="sunset"></i> Sunset</a> |
55 | | - </div></div> |
| 47 | + <aside class="sidebar sidebar-{$side}"> |
| 48 | + <div class="sidebar-header"><span><i data-lucide="{$icon}"></i> {$title}</span><button class="pin-toggle" data-sidebar="{$side}" title="Pin sidebar"><i data-lucide="pin"></i></button></div> |
| 49 | + <nav>{$nav}</nav> |
| 50 | + </aside> |
56 | 51 | HTML; |
57 | 52 | } |
58 | 53 |
|
59 | | - protected function html(string $theme, string $body): string |
| 54 | + private function html(string $body): string |
60 | 55 | { |
61 | 56 | return <<<HTML |
62 | 57 | <!DOCTYPE html> |
63 | 58 | <html lang="en"> |
64 | 59 | <head> |
65 | 60 | <meta charset="utf-8"> |
66 | 61 | <meta name="viewport" content="width=device-width, initial-scale=1"> |
67 | | - <title>{$this->out['doc']} [$theme]</title> |
| 62 | + <title>{$this->out['doc']}</title> |
68 | 63 | <link rel="stylesheet" href="/base.css"> |
69 | 64 | <link rel="stylesheet" href="/site.css"> |
70 | 65 | <script src="https://unpkg.com/lucide@latest/dist/umd/lucide.min.js"></script> |
71 | | - <script>(function(){const t=localStorage.getItem("base-theme"),s=localStorage.getItem("base-scheme"),c=t||(matchMedia("(prefers-color-scheme:dark)").matches?"dark":"light");document.documentElement.className=c+(s&&s!=="default"?" scheme-"+s:"")})();</script> |
| 66 | + <script>(function(){var s=JSON.parse(localStorage.getItem('base-state')||'{}'),t=s.theme,c=s.scheme,h=document.documentElement;h.className='preload '+(t||(matchMedia('(prefers-color-scheme:dark)').matches?'dark':'light'))+(c&&c!=='default'?' scheme-'+c:'');})()</script> |
72 | 67 | </head> |
73 | 68 | <body> |
74 | | - $body |
| 69 | + {$body} |
| 70 | + <div class="overlay"></div> |
75 | 71 | <script src="/base.js"></script> |
76 | 72 | </body> |
77 | 73 | </html> |
|
0 commit comments