55/**
66 * Class AdminTabs
77 *
8- * Drives the intra-page section nav for Pattern A admin routes — the
9- * routes that subdivide via `?section=<slug>` URLs (servers, mods,
10- * groups, settings, comms, admins, bans; see AGENTS.md "Sub-paged
11- * admin routes").
8+ * Two render shapes remain after #1490:
129 *
13- * #1259 unified the chrome
14- * ------------------------
15- * Pre-#1259 there were two visuals:
16- * - settings rendered a vertical 14rem sidebar inline in every
17- * `page_admin_settings_*.tpl`,
18- * - servers / mods / groups rendered a horizontal pill strip via
19- * `core/admin_tabs.tpl`.
20- * The horizontal strip read as "tabs into one document" rather than
21- * "navigation between sibling pages" even after #1239 routed each
22- * section to its own URL, and stacked badly on dense routes (mods has
23- * a 3-section strip already; admins family hits 4+). #1259 lifted the
24- * settings sidebar into the parameterized `core/admin_sidebar.tpl`
25- * partial and pointed every Pattern A handler at it via this class.
26- *
27- * #1239 — anchor links, no JS toggle
28- * ----------------------------------
29- * Pre-#1239 the partial emitted `<button onclick="openTab(this, …)">`
30- * elements that called a JS function from the v1.x sourcebans.js bulk
31- * file (removed at #1123 D1). Clicks were silent no-ops — every pane
32- * was visible permanently — until the broken chrome was repaired in
33- * #1239 by routing each section through its own `?section=<slug>` URL.
34- *
35- * Two render shapes
36- * -----------------
3710 * 1. `$tabs === []` (edit-* pages: admin.edit.ban.php, admin.rcon.php,
38- * admin.email.php, …):
11+ * admin.email.php, admin.export.php, …):
3912 * Emits `core/admin_tabs.tpl` which renders just the trailing
4013 * "Back" anchor — there's no sub-section nav for these surfaces,
41- * only the affordance to leave. This shape is unchanged by #1259.
42- *
43- * 2. `$tabs !== []` (Pattern A pages: admin.servers, admin.mods,
44- * admin.groups, admin.settings, admin.comms, admin.admins,
45- * admin.bans):
46- * Opens the sidebar shell (`<div class="admin-sidebar-shell">`),
47- * emits `core/admin_sidebar.tpl` (the <aside> + link list), then
48- * opens the content column (`<div class="admin-sidebar-content">`).
49- * The page handler is responsible for closing both wrappers AFTER
50- * `Renderer::render(...)` runs:
14+ * only the affordance to leave.
5115 *
52- * ```php
53- * new AdminTabs($sections, $userbank, $theme, $section, 'Settings sections');
54- * Renderer::render($theme, new AdminSettingsView(...));
55- * echo '</div></div><!-- /.admin-sidebar-content + /.admin-sidebar-shell -->';
56- * ```
16+ * 2. `$tabs !== []` (legacy Pattern A callers):
17+ * No-op. Section links live in the main sidebar accordion
18+ * (`AdminNavCatalog` + `core/navbar.tpl`, #1490). Page handlers
19+ * may still construct AdminTabs with a non-empty list during
20+ * migration; the constructor accepts the call and renders
21+ * nothing so content is not wrapped in a second rail.
5722 *
58- * The wrapper opens before the View and closes after, so each
59- * `Renderer::render` call slots into the content column without
60- * per-View structural changes.
61- *
62- * Each tab in the `$tabs` array carries:
63- * - `name` Display label rendered as the link text.
64- * - `permission` Bitmask for `CUserManager::HasAccess()`. The tab
65- * is omitted entirely when the current user lacks
66- * the flag.
67- * - `url` (required for non-empty tabs) The link target. The
68- * page handler is responsible for building it
69- * (typically `index.php?p=admin&c=<page>§ion=<slug>`)
70- * so the partial doesn't need to know about routing.
71- * - `slug` (required for non-empty tabs) Short identifier used
72- * for the `data-testid` and active-tab matching.
73- * - `icon` (optional) Lucide icon name (e.g. `server`,
74- * `puzzle`). When omitted the partial falls back to
75- * a generic `circle-dot` so every row has matching
76- * visual weight.
77- * - `config` (optional) Feature-toggle gate; the tab is omitted
78- * when `config` is set and falsy.
23+ * Prefer dropping non-empty `new AdminTabs(...)` calls from Pattern A
24+ * handlers entirely. Keep the empty-tabs Back-link shape.
7925 *
8026 * @phpstan-type TabSpec array{
8127 * name: string,
@@ -93,17 +39,10 @@ final class AdminTabs
9339
9440 /**
9541 * @param list<TabSpec> $tabs
96- * @param string|null $activeSlug Slug of the section to mark with
97- * `aria-current="page"`. When null, the first accessible tab's
98- * slug wins. When the value doesn't match any visible tab no
99- * tab is marked active (and the sidebar falls back to its
100- * all-inactive look — still better than every entry looking
101- * identical).
102- * @param string|null $sidebarLabel aria-label for the sidebar
103- * <aside>. Screen readers announce the navigation by this
104- * label ("Settings sections" / "Server sections" / …). Only
105- * consumed when `$tabs` is non-empty (the empty-tabs Back-link
106- * shape has no sidebar).
42+ * @param string|null $activeSlug Kept for call-site compatibility;
43+ * unused when `$tabs` is non-empty (main sidebar owns active state).
44+ * @param string|null $sidebarLabel Kept for call-site compatibility;
45+ * unused when `$tabs` is non-empty.
10746 */
10847 public function __construct (
10948 array $ tabs ,
@@ -128,30 +67,15 @@ public function __construct(
12867 }
12968
13069 if ($ this ->tabs === []) {
131- // Edit-* shape: just the trailing Back anchor. The legacy
132- // partial still owns this surface — there's no sidebar to
133- // unify, only the "leave this page" affordance.
13470 $ theme ->assign ('tabs ' , $ this ->tabs );
13571 $ theme ->assign ('active_tab ' , $ resolvedActive );
13672 $ theme ->display ('core/admin_tabs.tpl ' );
13773 return ;
13874 }
13975
140- // Sidebar shape (#1259). Open the shell + render the <aside> +
141- // open the content column. Closing tags live in the calling
142- // page handler — see the class docblock for the contract.
143- $ sidebarId = 'admin-sidebar ' ;
144- $ sidebarLabel = $ sidebarLabel !== null && $ sidebarLabel !== ''
145- ? $ sidebarLabel
146- : 'Page sections ' ;
147-
148- echo '<div class="admin-sidebar-shell" data-testid="admin-sidebar-shell"> ' ;
149- $ theme ->assign ('tabs ' , $ this ->tabs );
150- $ theme ->assign ('active_tab ' , $ resolvedActive );
151- $ theme ->assign ('sidebar_id ' , $ sidebarId );
152- $ theme ->assign ('sidebar_label ' , $ sidebarLabel );
153- $ theme ->display ('core/admin_sidebar.tpl ' );
154- echo '<div class="admin-sidebar-content"> ' ;
76+ // #1490 — section nav moved into the main sidebar accordion.
77+ // Non-empty AdminTabs no longer opens admin-sidebar-shell.
78+ unset($ sidebarLabel );
15579 }
15680}
15781
0 commit comments