Skip to content

Commit

Permalink
Merge pull request #1709 from hydephp/support-automatic-navigation-pr…
Browse files Browse the repository at this point in the history
…iorities-by-prefixing-a-number-in-filenames

[2.x] Support automatic navigation priorities by prefixing a number in filenames
  • Loading branch information
caendesilva authored May 4, 2024
2 parents af3cb6e + 8a28111 commit 5b1446d
Show file tree
Hide file tree
Showing 9 changed files with 651 additions and 2 deletions.
1 change: 1 addition & 0 deletions RELEASE_NOTES.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ This serves two purposes:
2. At release time, you can move the Unreleased section changes into a new release version section.

### Added
- You can now specify navigation priorities by adding a numeric prefix to the source file names in https://github.com/hydephp/develop/pull/1709
- Added a new `\Hyde\Framework\Actions\PreBuildTasks\TransferMediaAssets` build task handle media assets transfers for site builds.
- The `\Hyde\Facades\Features` class is no longer marked as internal, and is now thus part of the public API.

Expand Down
65 changes: 64 additions & 1 deletion docs/digging-deeper/navigation.md
Original file line number Diff line number Diff line change
Expand Up @@ -298,7 +298,70 @@ For example: `_docs/getting-started/installation.md` will be placed in a group c

Here are some things to keep in mind when using dropdown menus, regardless of the configuration:
- Dropdowns take priority over standard items. So if you have a dropdown with the key `about` and a page with the key `about`, the dropdown will be created, and the page won't be in the menu.
- For example: With this file structure: `_pages/foo.md`, `_pages/foo/bar.md`, `_pages/foo/baz.md`, the link to `foo` will be lost.
- For example: With this file structure: `_pages/foo.md`, `_pages/foo/bar.md`, `_pages/foo/baz.md`, the link to `foo` will be lost.

## Numerical Prefix Navigation Ordering

HydePHP v2 introduces a new feature that allows navigation items to be ordered based on a numerical prefix in the filename.
This is a great way to control the ordering of pages in both the primary navigation menu and the documentation sidebar,
as your file structure will match the order of the pages in the navigation menus.

For example, the following will have the same order in the navigation menu as in a file explorer:

```shell
_pages/
01-home.md # Gets priority 1, putting it first (will be saved to _site/index.html)
02-about.md # Gets priority 2, putting it second (will be saved to _site/about.html)
03-contact.md # Gets priority 3, putting it third (will be saved to _site/contact.html)
```

Hyde will then parse the number from the filename and use it as the priority for the page in the navigation menus.

### Keep in mind

Here are some things to keep in mind, especially if you mix numerical prefix ordering with other ordering methods:

1. The numerical prefix will still be part of the page identifier, but it will be stripped from the route key.
- For example: `_pages/01-home.md` will have the route key `home` and the page identifier `01-home`.
2. You can delimit the numerical prefix with either a dash or an underscore.
- For example: `_pages/01-home.md` and `_pages/01_home.md` are both valid.
3. The leading zeroes are optional, so `_pages/1-home.md` is also valid.

### Using numerical prefix ordering in subdirectories

The numerical prefix ordering feature works great when using the automatic subdirectory-based grouping for navigation menu dropdowns and documentation sidebar categories.

This integration has two main features to consider:
1. You can use numerical prefixes in subdirectories to control the order of dropdowns.
2. The ordering within a subdirectory works independently of its siblings, so you can start from one in each subdirectory.

Here is an example structure of how you may want to organize a documentation site:

```shell
_docs/
01-getting-started/
01-installation.md
02-requirements.md
03-configuration.md
02-usage/
01-quick-start.md
02-advanced-usage.md
03-features/
01-feature-1.md
02-feature-2.md
```

### Customization

You can disable this feature by setting the `numerical_page_ordering` setting to `false` in the `hyde.php` config file. Hyde will then no longer extract the priority and will no longer strip the prefix from the route key.

```php
// filepath config/hyde.php
'numerical_page_ordering' => false,
```

While it's not recommended, as you lose out on the convenience of the automatic ordering, any front matter priority settings will override the numerical prefix ordering if you for some reason need to.

## Digging Deeper into the internals

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
use Hyde\Framework\Factories\Concerns\CoreDataObject;
use Hyde\Framework\Features\Navigation\NavigationMenu;
use Hyde\Markdown\Contracts\FrontMatter\SubSchemas\NavigationSchema;
use Hyde\Framework\Features\Navigation\FilenamePrefixNavigationHelper;

use function basename;
use function array_flip;
Expand Down Expand Up @@ -97,6 +98,7 @@ protected function makePriority(): int
{
return $this->searchForPriorityInFrontMatter()
?? $this->searchForPriorityInConfigs()
?? $this->checkFilePrefixForOrder()
?? NavigationMenu::LAST;
}

Expand Down Expand Up @@ -239,6 +241,19 @@ private function parseNavigationPriorityConfig(array $config, string $pageKeyNam
return $config[$pageKey] ?? null;
}

private function checkFilePrefixForOrder(): ?int
{
if (! FilenamePrefixNavigationHelper::enabled()) {
return null;
}

if (! FilenamePrefixNavigationHelper::isIdentifierNumbered($this->identifier)) {
return null;
}

return FilenamePrefixNavigationHelper::splitNumberAndIdentifier($this->identifier)[0];
}

private function canUseSubdirectoryForGroups(): bool
{
return $this->getSubdirectoryConfiguration() === 'dropdown'
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
<?php

declare(strict_types=1);

namespace Hyde\Framework\Features\Navigation;

use Hyde\Facades\Config;
use Illuminate\Support\Str;

use function assert;
use function explode;
use function preg_match;

/**
* @internal Helper class for the new Filename Prefix Navigation feature.
*
* @experimental The code herein may be moved to more appropriate locations in the future.
*/
class FilenamePrefixNavigationHelper
{
/**
* Determines if the feature is enabled.
*/
public static function enabled(): bool
{
return Config::getBool('hyde.numerical_page_ordering', true);
}

/**
* Determines if a given identifier has a numerical prefix.
*/
public static function isIdentifierNumbered(string $identifier): bool
{
if (self::isIdentifierNested($identifier)) {
$identifier = self::getCoreIdentifierPart($identifier);
}

return preg_match('/^\d+-/', $identifier) === 1;
}

/**
* Splits a numbered identifier into its numerical prefix and the rest of the identifier.
*
* @return array{integer, string}
*/
public static function splitNumberAndIdentifier(string $identifier): array
{
if (self::isIdentifierNested($identifier)) {
$parentPath = self::getNestedIdentifierPrefix($identifier);
$identifier = self::getCoreIdentifierPart($identifier);
}

assert(self::isIdentifierNumbered($identifier));

$parts = explode('-', $identifier, 2);

$parts[0] = (int) $parts[0];

if (isset($parentPath)) {
$parts[1] = $parentPath.'/'.$parts[1];
}

return $parts;
}

protected static function isIdentifierNested(string $identifier): bool
{
return str_contains($identifier, '/');
}

protected static function getNestedIdentifierPrefix(string $identifier): string
{
assert(self::isIdentifierNested($identifier));

return Str::beforeLast($identifier, '/');
}

protected static function getCoreIdentifierPart(string $identifier): string
{
assert(self::isIdentifierNested($identifier));

return Str::afterLast($identifier, '/');
}
}
2 changes: 1 addition & 1 deletion packages/framework/src/Pages/DocumentationPage.php
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ public function getTableOfContents(): string
public function getRouteKey(): string
{
return Config::getBool('docs.flattened_output_paths', true)
? unslash(static::outputDirectory().'/'.basename($this->identifier))
? unslash(static::outputDirectory().'/'.basename(parent::getRouteKey()))
: parent::getRouteKey();
}

Expand Down
13 changes: 13 additions & 0 deletions packages/framework/src/Support/Models/RouteKey.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
namespace Hyde\Support\Models;

use Stringable;
use Hyde\Framework\Features\Navigation\FilenamePrefixNavigationHelper;

use function unslash;

Expand Down Expand Up @@ -47,6 +48,18 @@ public function get(): string
/** @param class-string<\Hyde\Pages\Concerns\HydePage> $pageClass */
public static function fromPage(string $pageClass, string $identifier): self
{
$identifier = self::splitNumberedIdentifiersIfNeeded($identifier);

return new self(unslash("{$pageClass::baseRouteKey()}/$identifier"));
}

/** @experimental */
protected static function splitNumberedIdentifiersIfNeeded(string $identifier): string
{
if (FilenamePrefixNavigationHelper::enabled() && FilenamePrefixNavigationHelper::isIdentifierNumbered($identifier)) {
$identifier = FilenamePrefixNavigationHelper::splitNumberAndIdentifier($identifier)[1];
}

return $identifier;
}
}
Loading

0 comments on commit 5b1446d

Please sign in to comment.