Skip to content

Commit e2a388e

Browse files
authored
✨ Add a make:navi view component generator command for Acorn (#76)
2 parents 94c2a8b + 3f13de8 commit e2a388e

File tree

8 files changed

+280
-50
lines changed

8 files changed

+280
-50
lines changed

README.md

+14
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,20 @@ $navigation->get()->name;
5757
$navigation->get('name', 'My menu title');
5858
```
5959

60+
### Acorn Usage
61+
62+
If you are using Navi alongside [Acorn](https://roots.io/acorn/) (e.g. Sage), you may generate a usable view component using Acorn's CLI:
63+
64+
```sh
65+
$ acorn make:navi
66+
```
67+
68+
Once generated, you may use the [view component](https://laravel.com/docs/11.x/blade#components) in an existing view like so:
69+
70+
```php
71+
<x-navigation />
72+
```
73+
6074
### Accessing Page Objects
6175

6276
If your menu item is linked to a page object (e.g. not a custom link) – you can retrieve the ID of the page using the `objectId` attribute.

examples/sage/app/View/Composers/Navigation.php

-26
This file was deleted.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
@props([
2+
'name' => null,
3+
'inactive' => 'hover:text-blue-500',
4+
'active' => 'text-blue-500',
5+
])
6+
7+
@php($menu = Navi::build($name))
8+
9+
@if ($menu->isNotEmpty())
10+
<ul {{ $attributes }}>
11+
@foreach ($menu->all() as $item)
12+
<li @class([
13+
$item->classes,
14+
$inactive => ! $item->active,
15+
$active => $item->active,
16+
])>
17+
<a href="{{ $item->url }}">
18+
{{ $item->label }}
19+
</a>
20+
21+
@if ($item->children)
22+
<ul>
23+
@foreach ($item->children as $child)
24+
<li @class([
25+
$child->classes,
26+
$inactive => ! $child->active,
27+
$active => $child->active,
28+
])>
29+
<a href="{{ $child->url }}">
30+
{{ $child->label }}
31+
</a>
32+
</li>
33+
@endforeach
34+
</ul>
35+
@endif
36+
</li>
37+
@endforeach
38+
</ul>
39+
@endif

examples/sage/resources/views/partials/navigation.blade.php

-23
This file was deleted.

src/Console/NaviMakeCommand.php

+165
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,165 @@
1+
<?php
2+
3+
namespace Log1x\Navi\Console;
4+
5+
use Illuminate\Console\GeneratorCommand;
6+
use Illuminate\Support\Facades\File;
7+
use Illuminate\Support\Str;
8+
use Symfony\Component\Console\Input\InputOption;
9+
10+
class NaviMakeCommand extends GeneratorCommand
11+
{
12+
/**
13+
* The console command description.
14+
*
15+
* @var string
16+
*/
17+
protected $description = 'Create a Navi component';
18+
19+
/**
20+
* The name and signature of the console command.
21+
*
22+
* @var string
23+
*/
24+
protected $name = 'make:navi';
25+
26+
/**
27+
* The type of file being generated.
28+
*
29+
* @var string
30+
*/
31+
protected $type = 'Component';
32+
33+
/**
34+
* Execute the console command.
35+
*
36+
* @return bool|null
37+
*/
38+
public function handle()
39+
{
40+
if (parent::handle() === false) {
41+
return false;
42+
}
43+
44+
$name = strtolower(trim($this->argument('name')));
45+
46+
$this->components->info("Navi component <fg=blue><x-{$name} /></> is ready for use.");
47+
}
48+
49+
/**
50+
* Build the class with the given name.
51+
*
52+
* @param string $name
53+
* @return string
54+
*/
55+
protected function buildClass($name)
56+
{
57+
$contents = parent::buildClass($name);
58+
59+
return str_replace(
60+
'{{ default }}',
61+
$this->option('default') ? Str::wrap($this->option('default'), "'") : 'null',
62+
$contents,
63+
);
64+
}
65+
66+
/**
67+
* Get the destination view path.
68+
*
69+
* @param string $name
70+
* @return string
71+
*/
72+
protected function getPath($name)
73+
{
74+
$path = $this->viewPath(
75+
str_replace('.', '/', 'components.'.$this->getView()).'.blade.php'
76+
);
77+
78+
if (! $this->files->isDirectory(dirname($path))) {
79+
$this->files->makeDirectory(dirname($path), 0777, true, true);
80+
}
81+
82+
return $path;
83+
}
84+
85+
/**
86+
* Get the view name relative to the components directory.
87+
*
88+
* @return string
89+
*/
90+
protected function getView()
91+
{
92+
$name = str_replace('\\', '/', $this->argument('name'));
93+
94+
return collect(explode('/', $name))
95+
->map(fn ($part) => Str::kebab($part))
96+
->implode('.');
97+
}
98+
99+
/**
100+
* Get the desired view name from the input.
101+
*
102+
* @return string
103+
*/
104+
protected function getNameInput()
105+
{
106+
$name = trim($this->argument('name'));
107+
108+
$name = str_replace(['\\', '.'], '/', $this->argument('name'));
109+
110+
return $name;
111+
}
112+
113+
/**
114+
* Get the stub file for the generator.
115+
*
116+
* @return string
117+
*/
118+
protected function getStub()
119+
{
120+
return $this->resolveStubPath(
121+
'/stubs/view.stub',
122+
);
123+
}
124+
125+
/**
126+
* Resolve the fully-qualified path to the stub.
127+
*
128+
* @param string $stub
129+
* @return string
130+
*/
131+
protected function resolveStubPath($stub)
132+
{
133+
return file_exists($customPath = $this->laravel->basePath(trim($stub, '/')))
134+
? $customPath
135+
: __DIR__.$stub;
136+
}
137+
138+
/**
139+
* Prompt for missing input arguments using the returned questions.
140+
*
141+
* @return array
142+
*/
143+
protected function promptForMissingArgumentsUsing()
144+
{
145+
return [
146+
'name' => [
147+
'What should the Navi component be named?',
148+
'E.g. Navigation',
149+
],
150+
];
151+
}
152+
153+
/**
154+
* Get the console command arguments.
155+
*
156+
* @return array
157+
*/
158+
protected function getOptions()
159+
{
160+
return [
161+
['default', 'd', InputOption::VALUE_OPTIONAL, 'The default menu name'],
162+
['force', 'f', InputOption::VALUE_NONE, 'Create the view component even if the component already exists'],
163+
];
164+
}
165+
}

src/Console/stubs/view.stub

+39
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
@props([
2+
'name' => {{ default }},
3+
'inactive' => 'hover:text-blue-500',
4+
'active' => 'text-blue-500',
5+
])
6+
7+
@php($menu = Navi::build($name))
8+
9+
@if ($menu->isNotEmpty())
10+
<ul {{ $attributes }}>
11+
@foreach ($menu->all() as $item)
12+
<li @class([
13+
$item->classes,
14+
$inactive => ! $item->active,
15+
$active => $item->active,
16+
])>
17+
<a href="{{ $item->url }}">
18+
{{ $item->label }}
19+
</a>
20+
21+
@if ($item->children)
22+
<ul>
23+
@foreach ($item->children as $child)
24+
<li @class([
25+
$child->classes,
26+
$inactive => ! $child->active,
27+
$active => $child->active,
28+
])>
29+
<a href="{{ $child->url }}">
30+
{{ $child->label }}
31+
</a>
32+
</li>
33+
@endforeach
34+
</ul>
35+
@endif
36+
</li>
37+
@endforeach
38+
</ul>
39+
@endif

src/Navi.php

+8-1
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,11 @@ class Navi
1414
*/
1515
protected array $items = [];
1616

17+
/**
18+
* The default menu.
19+
*/
20+
protected string $default = 'primary_navigation';
21+
1722
/**
1823
* Create a new Navi instance.
1924
*/
@@ -35,8 +40,10 @@ public static function make(array $items = []): self
3540
/**
3641
* Build the navigation menu items.
3742
*/
38-
public function build(string $menu = 'primary_navigation'): self
43+
public function build(mixed $menu = null): self
3944
{
45+
$menu = $menu ?? $this->default;
46+
4047
if (is_string($menu)) {
4148
$locations = get_nav_menu_locations();
4249

src/Providers/NaviServiceProvider.php

+15
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
namespace Log1x\Navi\Providers;
44

55
use Illuminate\Support\ServiceProvider;
6+
use Log1x\Navi\Console\NaviMakeCommand;
67
use Log1x\Navi\Navi;
78

89
class NaviServiceProvider extends ServiceProvider
@@ -16,4 +17,18 @@ public function register()
1617
{
1718
$this->app->bind('navi', fn () => Navi::make());
1819
}
20+
21+
/**
22+
* Bootstrap any application services.
23+
*
24+
* @return void
25+
*/
26+
public function boot()
27+
{
28+
if ($this->app->runningInConsole()) {
29+
$this->commands([
30+
NaviMakeCommand::class,
31+
]);
32+
}
33+
}
1934
}

0 commit comments

Comments
 (0)