Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
60 commits
Select commit Hold shift + click to select a range
66da2e4
Deprecate internal helper method
emmadesilva Feb 21, 2024
2695ad3
Add outer conditional
emmadesilva Feb 21, 2024
657462f
Add todo
emmadesilva Feb 21, 2024
e864054
Add new helper
emmadesilva Feb 21, 2024
b0f682f
Add inner conditional
emmadesilva Feb 21, 2024
e7a9f6f
Begin porting deterministic generation logic
emmadesilva Feb 21, 2024
f505764
Remove now unused helpers
emmadesilva Feb 21, 2024
68f0d95
Revert "Add todo"
emmadesilva Feb 21, 2024
2171de1
Rename helpers to be more generic
emmadesilva Feb 21, 2024
de80690
Merge with outer condition
emmadesilva Feb 21, 2024
15621bf
Replace closure return with else branch
emmadesilva Feb 21, 2024
6f0e46d
Inline local variable
emmadesilva Feb 21, 2024
742ba39
Shift item construction to normalize method signature
emmadesilva Feb 21, 2024
cf175b6
Normalize method implementation
emmadesilva Feb 21, 2024
b96a898
Use the local variable
emmadesilva Feb 21, 2024
034cf3f
Create BaseMenuGenerator.php
emmadesilva Feb 21, 2024
d6649f0
Make class abstract
emmadesilva Feb 21, 2024
eef96d7
Mark class as experimental
emmadesilva Feb 21, 2024
9132c24
Cover new base class
emmadesilva Feb 21, 2024
276f176
Extend new base class
emmadesilva Feb 21, 2024
3a8d108
Move shared property to base class
emmadesilva Feb 21, 2024
f3a99ba
Move routes property to base class
emmadesilva Feb 21, 2024
5609efa
Move constructors to base class
emmadesilva Feb 21, 2024
b2af8b4
Use routes property
emmadesilva Feb 21, 2024
aa33de8
Fix route construction logic
emmadesilva Feb 21, 2024
f4b336e
Simplify 'if'
emmadesilva Feb 21, 2024
116ca5a
Refactor to move entry point to base class
emmadesilva Feb 21, 2024
65dbb89
Introduce state field
emmadesilva Feb 21, 2024
19cfb69
Contract abstract function
emmadesilva Feb 21, 2024
5ae81ea
Introduce polymorphic state field
emmadesilva Feb 21, 2024
15453da
Extract helper method
emmadesilva Feb 21, 2024
4ef3834
Replace '?:' with if
emmadesilva Feb 21, 2024
ce2e9dd
Use added state field
emmadesilva Feb 21, 2024
8bb7552
Inline polymorphic helper methods
emmadesilva Feb 21, 2024
5d9e82a
Convert closure to arrow function
emmadesilva Feb 21, 2024
2778649
Clarify logic description
emmadesilva Feb 21, 2024
7c7f582
Format code comment
emmadesilva Feb 21, 2024
a4ab7ad
Inline local variable
emmadesilva Feb 21, 2024
ae551a4
Document reasoning behind filter
emmadesilva Feb 21, 2024
31f7e62
Document reasoning behind filter
emmadesilva Feb 21, 2024
03599ca
Extract parent method
emmadesilva Feb 21, 2024
8b2760c
Swap method source order
emmadesilva Feb 21, 2024
9e01c8d
Add abstract method stub
emmadesilva Feb 21, 2024
d67c695
Rename helper method
emmadesilva Feb 21, 2024
d50c054
Refactor to add parent method
emmadesilva Feb 21, 2024
ba428a8
Add shared logic to parent class
emmadesilva Feb 21, 2024
0832057
Refactor to bring logic to parent
emmadesilva Feb 21, 2024
a0a64aa
Simplify 'if'
emmadesilva Feb 21, 2024
800d198
Add clarifying parentheses
emmadesilva Feb 21, 2024
e759d5c
Move shared method to parent
emmadesilva Feb 21, 2024
d40f255
Begin refactoring method to parent class
emmadesilva Feb 21, 2024
b57a549
Extract common parts from if/else block
emmadesilva Feb 21, 2024
d6c33ae
Simplify 'if'
emmadesilva Feb 21, 2024
338b453
Normalize method signatures
emmadesilva Feb 21, 2024
9af0fbc
Move shared method into base class
emmadesilva Feb 21, 2024
d167660
Change static method to instance member
emmadesilva Feb 21, 2024
5ee8ce1
Refactor to move method into parent class
emmadesilva Feb 21, 2024
351ff64
Refactor to move method into parent class
emmadesilva Feb 21, 2024
02b327c
Normalize helper method signatures
emmadesilva Feb 21, 2024
f14c7dd
Remove resolved todos
emmadesilva Feb 21, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
<?php

declare(strict_types=1);

namespace Hyde\Framework\Features\Navigation;

use Hyde\Hyde;
use Hyde\Facades\Config;
use Illuminate\Support\Str;
use Hyde\Support\Models\Route;
use Hyde\Pages\DocumentationPage;
use Illuminate\Support\Collection;
use Hyde\Foundation\Facades\Routes;
use Hyde\Foundation\Kernel\RouteCollection;

use function filled;
use function strtolower;

/**
* @experimental This class may change significantly before its release.
*/
abstract class BaseMenuGenerator
{
/** @var \Illuminate\Support\Collection<string, \Hyde\Framework\Features\Navigation\NavItem> */
protected Collection $items;

/** @var \Hyde\Foundation\Kernel\RouteCollection<string, \Hyde\Support\Models\Route> */
protected RouteCollection $routes;

protected bool $generatesSidebar;
protected bool $usesGroups;

protected function __construct()
{
$this->items = new Collection();

$this->generatesSidebar = $this instanceof GeneratesDocumentationSidebarMenu;

$this->routes = $this->generatesSidebar
? Routes::getRoutes(DocumentationPage::class)
: Routes::all();

$this->usesGroups = $this->usesGroups();
}

public static function handle(): NavigationMenu
{
$menu = new static();

$menu->generate();

return new NavigationMenu($menu->items);
}

protected function generate(): void
{
$this->routes->each(function (Route $route): void {
if ($this->canAddRoute($route)) {
if ($this->canGroupRoute($route)) {
$this->addRouteToGroup($route);
} else {
$this->items->put($route->getRouteKey(), NavItem::fromRoute($route));
}
}
});
}

protected function usesGroups(): bool
{
if ($this->generatesSidebar) {
// In order to know if we should use groups in the sidebar, we need to loop through the pages and see if they have a group set.
// This automatically enables the sidebar grouping for all pages if at least one group is set.

return $this->routes->first(fn (Route $route): bool => filled($route->getPage()->navigationMenuGroup())) !== null;
} else {
return Config::getString('hyde.navigation.subdirectories', 'hidden') === 'dropdown';
}
}

protected function canAddRoute(Route $route): bool
{
return $route->getPage()->showInNavigation();
}

protected function canGroupRoute(Route $route): bool
{
return $this->usesGroups;
}

protected function addRouteToGroup(Route $route): void
{
$item = NavItem::fromRoute($route);

$groupName = $this->generatesSidebar ? ($item->getGroup() ?? 'Other') : $item->getGroup();

$groupItem = $this->getOrCreateGroupItem($groupName);

$groupItem->addChild($item);

if (! $this->items->has($groupItem->getIdentifier())) {
$this->items->put($groupItem->getIdentifier(), $groupItem);
}
}

protected function getOrCreateGroupItem(string $groupName): NavItem
{
$groupKey = Str::slug($groupName);
$group = $this->items->get($groupKey);

return $group ?? $this->createGroupItem($groupKey, $groupName);
}

protected function createGroupItem(string $groupKey, string $groupName): NavItem
{
$label = $this->searchForGroupLabelInConfig($groupKey) ?? $groupName;

$priority = $this->searchForGroupPriorityInConfig($groupKey);

return NavItem::dropdown($this->normalizeGroupLabel($label), [], $priority);
}

protected function normalizeGroupLabel(string $label): string
{
// If there is no label, and the group is a slug, we can make a title from it
if ($label === strtolower($label)) {
return Hyde::makeTitle($label);
}

return $label;
}

protected function searchForGroupLabelInConfig(string $groupKey): ?string
{
$key = $this->generatesSidebar ? 'docs.sidebar_group_labels' : 'hyde.navigation.labels';

return Config::getArray($key, [])[$groupKey] ?? null;
}

protected function searchForGroupPriorityInConfig(string $groupKey): ?int
{
$key = $this->generatesSidebar ? 'docs.sidebar_order' : 'hyde.navigation.order';

return Config::getArray($key, [])[$groupKey] ?? null;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,40 +4,18 @@

namespace Hyde\Framework\Features\Navigation;

use Hyde\Hyde;
use Hyde\Facades\Config;
use Illuminate\Support\Str;
use Hyde\Support\Models\Route;
use Hyde\Pages\DocumentationPage;
use Illuminate\Support\Collection;
use Hyde\Foundation\Facades\Routes;
use Hyde\Foundation\Kernel\RouteCollection;

use function filled;
use function collect;
use function strtolower;

/**
* @experimental This class may change significantly before its release.
*
* @todo Consider making into a service which can create the sidebar as well.
*
* @see \Hyde\Framework\Features\Navigation\GeneratesMainNavigationMenu
*/
class GeneratesDocumentationSidebarMenu
class GeneratesDocumentationSidebarMenu extends BaseMenuGenerator
{
/** @var \Illuminate\Support\Collection<string, \Hyde\Framework\Features\Navigation\NavItem> */
protected Collection $items;

/** @var \Hyde\Foundation\Kernel\RouteCollection<string, \Hyde\Support\Models\Route> */
protected RouteCollection $routes;

protected function __construct()
{
$this->items = new Collection();
$this->routes = Routes::getRoutes(DocumentationPage::class);
}

public static function handle(): DocumentationSidebar
{
$menu = new static();
Expand All @@ -50,39 +28,19 @@ public static function handle(): DocumentationSidebar

protected function generate(): void
{
$useGroups = $this->usesSidebarGroups();

$this->routes->each(function (Route $route) use ($useGroups): void {
if ($this->canAddRoute($route)) {
$item = NavItem::fromRoute($route);

if ($useGroups) {
$this->addItemToGroup($item);
} else {
$this->items->put($route->getRouteKey(), $item);
}
}
});
parent::generate();

// If there are no pages other than the index page, we add it to the sidebar so that it's not empty
if ($this->items->count() === 0 && DocumentationPage::home() !== null) {
$this->items->push(NavItem::fromRoute(DocumentationPage::home()));
}
}

protected function usesSidebarGroups(): bool
{
// In order to know if we should use groups in the sidebar,
// we need to loop through the pages and see if they have a group set

return $this->routes->first(function (Route $route): bool {
return filled($route->getPage()->navigationMenuGroup());
}) !== null;
}

protected function canAddRoute(Route $route): bool
{
return $route->getPage()->showInNavigation() && ! $route->is(DocumentationPage::homeRouteName());
return parent::canAddRoute($route)
// Since the index page is linked in the header, we don't want it in the sidebar
&& ! $route->is(DocumentationPage::homeRouteName());
}

protected function sortByPriority(): void
Expand All @@ -97,57 +55,8 @@ protected function sortByPriority(): void
})->values();
}

protected function addItemToGroup(NavItem $item): void
{
$groupItem = $this->getOrCreateGroupItem($item->getGroup() ?? 'Other');

$groupItem->addChild($item);

if (! $this->items->has($groupItem->getIdentifier())) {
$this->items->put($groupItem->getIdentifier(), $groupItem);
}
}

protected function getLowestPriorityInGroup(NavItem $item): int
{
return collect($item->getChildren())->min(fn (NavItem $child): int => $child->getPriority());
}

protected function getOrCreateGroupItem(string $groupName): NavItem
{
$identifier = Str::slug($groupName);
$group = $this->items->get($identifier);

return $group ?? $this->createGroupItem($identifier, $groupName);
}

protected function createGroupItem(string $identifier, string $groupName): NavItem
{
$label = $this->searchForGroupLabelInConfig($identifier) ?? $groupName;
$priority = $this->searchForGroupPriorityInConfig($identifier);

return NavItem::dropdown(static::normalizeGroupLabel($label), [], $priority);
}

protected function searchForGroupLabelInConfig(string $identifier): ?string
{
return Config::getArray('docs.sidebar_group_labels', [])[$identifier] ?? null;
}

/** Todo: Move into shared class */
protected static function normalizeGroupLabel(string $label): string
{
// If there is no label, and the group is a slug, we can make a title from it
if ($label === strtolower($label)) {
return Hyde::makeTitle($label);
}

return $label;
}

/** Todo: Move into shared class */
protected static function searchForGroupPriorityInConfig(string $groupKey): ?int
{
return Config::getArray('docs.sidebar_order', [])[$groupKey] ?? null;
}
}
Loading