From 0113d2e9c9ff34a6ffb10707cc76d34b67a0c367 Mon Sep 17 00:00:00 2001
From: Jake Vanderwerf <get@jakevanderwerf.ca>
Date: Mon, 19 Jan 2026 16:29:41 +0000
Subject: [PATCH] =Refactored window.getTemplate into a full templating class window.jvbTemplates. Refactored CRUD.js, UploadManager.js, FormController.js, PopulateForm.js with that in mind
---
inc/ui/Navigation.php | 176 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
1 files changed, 171 insertions(+), 5 deletions(-)
diff --git a/inc/ui/Navigation.php b/inc/ui/Navigation.php
index 7988917..2fc120d 100644
--- a/inc/ui/Navigation.php
+++ b/inc/ui/Navigation.php
@@ -23,13 +23,15 @@
*/
class Navigation {
private string $id;
- private array $items = [];
+ public array $items = [];
private array $classes = [];
protected array $defaultMenuClasses = [];
private bool $isNav = true;
private bool $hasToggle = false;
protected array $defaultItemClasses = [];
private int $counter = 0;
+ private bool $isDrawer = false;
+ private bool $drawerCollapsed = true;
public function __construct(string $id = '') {
$this->id = $id ?: 'menu-' . uniqid();
@@ -124,11 +126,19 @@
if ($this->isNav) {
$html = '<nav id="' . esc_attr($this->id).'"' . $classStr.'>';
- if ($this->hasToggle) {
+ // Drawer toggle or regular toggle
+ if ($this->isDrawer) {
+ $html .= '<button class="toggle main" type="button"
+ aria-expanded="' . ($this->drawerCollapsed ? 'false' : 'true') . '"
+ aria-controls="' . esc_attr($this->id) . '-list">
+ ' . jvbIcon('caret-left') . '
+ <span class="screen-reader-text">Toggle Menu</span>
+ </button>';
+ } elseif ($this->hasToggle) {
$html .= '<button class="toggle main" type="button" aria-expanded="false" aria-controls="' . esc_attr($this->id) . '">
- ' . jvbIcon('list') . '
- <span class="screen-reader-text">Toggle Menu</span>
- </button>';
+ ' . jvbIcon('list') . '
+ <span class="screen-reader-text">Toggle Menu</span>
+ </button>';
}
}
if (!$this->isNav) {
@@ -154,6 +164,96 @@
echo $html;
return $html;
}
+
+ /**
+ * Configure as a drawer-style menu
+ *
+ * @param bool $collapsed Initial state
+ * @return self
+ */
+ public function asDrawer(bool $collapsed = true): self {
+ $this->isDrawer = true;
+ $this->drawerCollapsed = $collapsed;
+ $this->addClass('drawer');
+ if (!$collapsed) {
+ $this->addClass('open');
+ }
+ return $this;
+ }
+
+ public function isDrawer(): bool {
+ return $this->isDrawer;
+ }
+ /**
+ * Add a section header
+ *
+ * @param string $title
+ * @return MenuSection
+ */
+ public function addSection(string $title): MenuSection {
+ $section = new MenuSection($title, ++$this->counter);
+ $this->items[] = $section;
+
+ if (!empty($this->defaultItemClasses)) {
+ foreach ($this->defaultItemClasses as $class) {
+ $section->addItemClass($class);
+ }
+ }
+
+ return $section;
+ }
+
+ /**
+ * Populate menu from array structure
+ *
+ * @param array $items Array of menu items
+ * @return self
+ */
+ public function populateFromArray(array $items): self {
+ foreach ($items as $item) {
+ // Handle sections
+ if (!empty($item['section'])) {
+ $section = $this->addSection($item['section']);
+ if (!empty($item['items'])) {
+ $this->populateSection($section, $item['items']);
+ }
+ continue;
+ }
+
+ // Handle regular items
+ $menuItem = $this->addItem($item['text'] ?? '', $item['icon'] ?? '');
+
+ if (!empty($item['class'])) {
+ $menuItem->addClass($item['class']);
+ }
+ if (!empty($item['url'])) {
+ $menuItem->url($item['url']);
+ }
+
+
+ if (!empty($item['submenu'])) {
+ $submenu = $menuItem->submenu();
+ $submenu->populateFromArray($item['submenu']);
+ }
+ }
+
+ return $this;
+ }
+
+ private function populateSection(MenuSection $section, array $items): void {
+ foreach ($items as $item) {
+ $menuItem = $section->addItem($item['text'] ?? '', $item['icon'] ?? null);
+
+ if (!empty($item['url'])) {
+ $menuItem->url($item['url']);
+ }
+
+ if (!empty($item['submenu'])) {
+ $submenu = $menuItem->submenu();
+ $submenu->populateFromArray($item['submenu']);
+ }
+ }
+ }
}
/**
@@ -276,6 +376,9 @@
* @return string
*/
public function render(): string {
+ if (!$this->url && (!$this->submenu || empty($this->submenu->items))) {
+ return '';
+ }
$classes = $this->classes;
if ($this->submenu) {
$classes[] = 'has-submenu';
@@ -324,3 +427,66 @@
return $html;
}
}
+
+/**
+ * Menu section with header and items
+ */
+class MenuSection {
+ private int $id;
+ private string $title;
+ private array $items = [];
+ private array $classes = [];
+ private array $defaultItemClasses = [];
+ private int $counter = 0;
+
+ public function __construct(string $title, int $id) {
+ $this->title = $title;
+ $this->id = $id;
+ }
+
+ public function addItem(?string $text = null, ?string $icon = null): MenuItem {
+ $item = new MenuItem(++$this->counter);
+ $this->items[] = $item;
+
+ if ($text) $item->text($text);
+ if ($icon) $item->icon($icon);
+
+ if (!empty($this->defaultItemClasses)) {
+ foreach ($this->defaultItemClasses as $class) {
+ $item->addClass($class);
+ }
+ }
+
+ return $item;
+ }
+
+ public function addClass(string $class): self {
+ $this->classes[] = $class;
+ return $this;
+ }
+
+ public function addItemClass(string $class): self {
+ $this->defaultItemClasses[] = $class;
+ return $this;
+ }
+
+ public function render(): string {
+ if (empty($this->items)) {
+ return '';
+ }
+
+ $classStr = !empty($this->classes) ? ' class="menu-section ' . esc_attr(implode(' ', $this->classes)) . '"' : ' class="menu-section"';
+
+ $html = '<li' . $classStr . '>';
+ $html .= '<span class="section-title">' . esc_html($this->title) . '</span>';
+ $html .= '<ul class="section-items">';
+
+ foreach ($this->items as $item) {
+ $html .= $item->render();
+ }
+
+ $html .= '</ul></li>';
+
+ return $html;
+ }
+}
--
Gitblit v1.10.0