diff --git a/index.php b/index.php index 6cee188..c4284c0 100644 --- a/index.php +++ b/index.php @@ -53,6 +53,7 @@ if ($photo && $activeSectionId < 1) { $comments = $photo ? commentsByPhoto($activePhotoId) : []; $topics = []; $topicCounts = []; +$topicTree = []; try { $topics = topicsAllForSelect(); if ($activeTopicId > 0) { @@ -61,9 +62,11 @@ try { } } $topicCounts = topicPhotoCounts($activeSectionId > 0 ? $activeSectionId : null); + $topicTree = buildTopicTreePublic($topics); } catch (Throwable) { $topics = []; $topicCounts = []; + $topicTree = []; $activeTopicId = 0; } @@ -112,6 +115,8 @@ if ($photo) { } $hasMobilePhotoNav = $activePhotoId > 0 && $photo && $detailTotal > 0; +$isTopicMode = $activeTopicId > 0; +$isSectionMode = !$isTopicMode && $activeSectionId > 0; $bodyClasses = [$isHomePage ? 'is-home' : 'is-inner']; if ($hasMobilePhotoNav) { $bodyClasses[] = 'has-mobile-nav'; @@ -121,6 +126,31 @@ function h(string $v): string { return htmlspecialchars($v, ENT_QUOTES | ENT_SUB function assetUrl(string $path): string { $f=__DIR__ . '/' . ltrim($path,'/'); $v=is_file($f)?(string)filemtime($f):(string)time(); return $path . '?v=' . rawurlencode($v); } function limitText(string $text, int $len): string { return function_exists('mb_substr') ? mb_substr($text, 0, $len) : substr($text, 0, $len); } +function buildTopicTreePublic(array $topics): array +{ + $roots = []; + $children = []; + + foreach ($topics as $topic) { + $parentId = isset($topic['parent_id']) && $topic['parent_id'] !== null ? (int)$topic['parent_id'] : 0; + if ($parentId === 0) { + $roots[] = $topic; + continue; + } + if (!isset($children[$parentId])) { + $children[$parentId] = []; + } + $children[$parentId][] = $topic; + } + + foreach ($roots as &$root) { + $root['children'] = $children[(int)$root['id']] ?? []; + } + unset($root); + + return $roots; +} + function serveImage(): never { $fileId = (int)($_GET['file_id'] ?? 0); @@ -228,14 +258,16 @@ function outputWatermarked(string $path, string $mime): never .page{display:grid;gap:16px;grid-template-columns:300px minmax(0,1fr)} .panel{background:#fff;border:1px solid #e5e7eb;border-radius:12px;padding:14px} .sidebar{position:sticky;top:14px;align-self:start;max-height:calc(100dvh - 28px);overflow:auto} - .sec{display:grid;gap:6px} - .sec a{display:block;padding:10px 12px;border-radius:10px;line-height:1.35;text-decoration:none;color:#111} - .sec a.active{background:#eef4ff;color:#1f6feb} - .topic-nav{margin-top:12px;padding-top:12px;border-top:1px solid #e8edf5;display:grid;gap:6px} - .topic-link{display:block;padding:8px 10px;border-radius:8px;text-decoration:none;color:#111;font-size:13px;line-height:1.3} - .topic-link.level-1{padding-left:20px} - .topic-link.active{background:#edf7f0;color:#146236} - .topic-link.disabled{opacity:.55} + .nav-group{border-top:1px solid #e8edf5;padding-top:10px;margin-top:10px} + .nav-group:first-of-type{border-top:0;margin-top:0;padding-top:0} + .nav-summary{cursor:pointer;list-style:none;font-size:13px;font-weight:700;color:#374151;display:flex;align-items:center;justify-content:space-between;gap:8px} + .nav-summary::-webkit-details-marker{display:none} + .nav-summary::after{content:'▾';font-size:12px;color:#6b7280;transition:transform .18s ease} + .nav-group:not([open]) .nav-summary::after{transform:rotate(-90deg)} + .nav-list{display:grid;gap:6px;margin-top:8px} + .nav-link{display:block;padding:10px 12px;border-radius:10px;line-height:1.35;text-decoration:none;color:#111;font-size:13px} + .nav-link.level-1{padding-left:24px} + .nav-link.active{background:#eef4ff;color:#1f6feb} .cards{display:grid;gap:10px;grid-template-columns:repeat(auto-fill,minmax(180px,1fr))} .card{border:1px solid #e5e7eb;border-radius:10px;overflow:hidden;background:#fff} .card-badges{position:absolute;top:8px;right:8px;display:flex;gap:6px;flex-wrap:wrap;justify-content:flex-end;z-index:4;pointer-events:none} @@ -270,8 +302,7 @@ function outputWatermarked(string $path, string $mime): never .sidebar-backdrop{display:none} @media (max-width:900px){ - .topbar{display:flex;align-items:center;justify-content:space-between;gap:10px} - .topbar h1{margin:0;font-size:24px} + .topbar{display:flex;align-items:center;justify-content:flex-end;gap:10px} .page{grid-template-columns:1fr} .sidebar{position:static;max-height:none} .pager{display:none} @@ -290,43 +321,53 @@ function outputWatermarked(string $path, string $mime): never @media (max-width:560px){ .app{padding:14px} - .topbar h1{font-size:22px} }