mergeConfigFrom(__DIR__.'/../config/livewire-material.php', 'livewire-material'); $this->disableBladeIconsComponent(); $this->registerErrorViews(); $this->registerMailComponents(); } public function boot(): void { $this->loadViewsFrom(__DIR__.'/../resources/views', 'livewire-material'); $this->registerComponents(); $this->registerPagination(); $this->registerShowcase(); if ($this->app->runningInConsole()) { $this->commands([ Console\SchemeCommand::class, ]); $this->publishes([ __DIR__.'/../config/livewire-material.php' => config_path('livewire-material.php'), ], 'livewire-material-config'); $this->publishes([ static::errorViewPath().'/errors' => resource_path('views/errors'), ], 'livewire-material-errors'); $this->publishes([ static::mailComponentPath().'/html' => resource_path('views/vendor/mail/html'), static::mailComponentPath().'/text' => resource_path('views/vendor/mail/text'), ], 'livewire-material-mail'); } } /** * The anonymous components. */ public static function componentPath(): string { return __DIR__.'/../resources/views/components'; } /** * The error-view root: a folder holding only `errors/`. */ public static function errorViewPath(): string { return dirname(__DIR__).'/resources/views/error-pages'; } /** * The mail component root, laid out like the framework's: `html/` and `text/`. */ public static function mailComponentPath(): string { return dirname(__DIR__).'/resources/views/mail'; } /** * Append the error-view root to `view.paths`, after the application's own. * * The exception handler replaces the `errors` namespace each time it renders an HTTP * exception, with every entry of `view.paths` plus `/errors` and the framework's views last * (RegisterErrorViewPaths), so a namespace added with addNamespace() is gone by then; only a * view path survives. That config is read at render time, but also once when the view * finder is built: appended here, in register(), before any boot() resolves the view * factory, the finder and `view:cache` see the same paths the handler does. Appending keeps * `view.paths[0]` — Laravel's viewPath() — the application's, and puts the application's * own `resources/views/errors` first. Idempotent, since a cached config already holds it. */ protected function registerErrorViews(): void { $paths = (array) config('view.paths', []); if (! in_array(static::errorViewPath(), $paths, true)) { config(['view.paths' => [...$paths, static::errorViewPath()]]); } } /** * Put the package's mail header and message after the application's own mail components, * when `livewire-material.mail.components` asks for it. Off by default: it changes every * Markdown mail the application sends, theme or not, and the framework reads * `mail.markdown.paths` once, when the Markdown renderer is first made — after register(). */ protected function registerMailComponents(): void { if (! config('livewire-material.mail.components')) { return; } $paths = (array) config('mail.markdown.paths', []); if (! in_array(static::mailComponentPath(), $paths, true)) { config(['mail.markdown.paths' => [...$paths, static::mailComponentPath()]]); } } /** * Register the components as anonymous Blade components under the configured prefix, or * under `livewire-material` without one. * * A compiled `` names the view namespace Blade derives from the prefix, or from the * component folder's absolute path when there is none. That path differs between a laptop and * a container sharing `storage/framework/views`, or between release directories, while the * compiled file's name does not — so a view compiled on one showed `a1b2…::button` as text on * the other. A prefix does not stop an unprefixed tag resolving. * * The package's own views never go through this registration: they write * ``, which resolves through the view namespace whatever the * prefix, and which an application's own `` cannot shadow. */ protected function registerComponents(): void { Blade::anonymousComponentPath( static::componentPath(), filled(config('livewire-material.prefix')) ? config('livewire-material.prefix') : 'livewire-material', ); } /** * Put the M3 paginators in front of Laravel's and Livewire's own, and take over both * frameworks' default theme so no application forgets the config and silently renders * Laravel's or Livewire's own Tailwind view instead. * * The namespaces still go through callAfterResolving('view', …): the Factory instance only * exists once the `view` service resolves, which may happen before or after this method runs. * `Paginator::defaultView()`/`defaultSimpleView()` and `livewire.pagination_theme`, in * contrast, are a plain static and a config value — nothing needs resolving first — so they * are set here directly, during this provider's boot(). Package providers boot before the * application's own (its `bootstrap/providers.php` list is appended after the discovered * package providers), so an application that calls `Paginator::defaultView()` again, or sets * `livewire.pagination_theme` itself, in its own boot() still wins. Deferring them into the * callback instead would risk the opposite: `view` can resolve arbitrarily late — the first * render, possibly after the application's own boot() already set its override — and this * call would silently overwrite it. * * `livewire.pagination_theme` is only taken over while it still reads as Livewire's own * default, `tailwind` — including when the key is missing entirely, since * `SupportPagination::paginationView()` reads it with that same fallback. An application that * chose another theme, or already set `material`, keeps it. Either way, a component's own * `$paginationTheme` property or `paginationView()` method still wins over the config, because * Livewire checks those first. */ protected function registerPagination(): void { if (! config('livewire-material.pagination')) { return; } $this->callAfterResolving('view', function (Factory $view): void { $view->prependNamespace('pagination', __DIR__.'/../resources/views/pagination/laravel'); $view->prependNamespace('livewire', __DIR__.'/../resources/views/pagination/livewire'); }); Paginator::defaultView('pagination::material'); Paginator::defaultSimpleView('pagination::simple-material'); if (config('livewire.pagination_theme', 'tailwind') === 'tailwind') { config(['livewire.pagination_theme' => 'material']); } } /** * blade-icons registers a class-based of its own, and Blade resolves a * registered class alias before any anonymous component path, so ours would never * render. It reads the key in its boot(); a booting callback runs after every * register() — so after blade-icons merges its defaults over the key — and before * any provider boots. */ protected function disableBladeIconsComponent(): void { $this->app->booting(function (): void { config(['blade-icons.components.default' => null]); }); } /** * Mount the showcase when it is enabled. */ protected function registerShowcase(): void { // Registered even with the showcase off: `php artisan view:cache` compiles every view in the // package's namespace, the showcase's pages included, and must resolve there. Blade::anonymousComponentPath(__DIR__.'/../resources/views/showcase/components', 'showcase'); if (! config('livewire-material.showcase.enabled')) { return; } if ($this->app->routesAreCached()) { return; } Route::middleware(config('livewire-material.showcase.middleware')) ->prefix(config('livewire-material.showcase.path')) ->name('livewire-material.') ->group(__DIR__.'/../routes/showcase.php'); } }