diff --git a/.ai/hotwire.blade.php b/resources/boost/guidelines/core.blade.php similarity index 68% rename from .ai/hotwire.blade.php rename to resources/boost/guidelines/core.blade.php index 91d70f2..96cd948 100644 --- a/.ai/hotwire.blade.php +++ b/resources/boost/guidelines/core.blade.php @@ -1,40 +1,36 @@ ## Hotwire/Turbo Core Principles - For standard application development, use Hotwire (Turbo + Stimulus) -- Send HTML over the wire instead of JSON. Keep complexity on the server side. -- Use Turbo Drive for smooth page transitions without full page reloads. -- Decompose pages with Turbo Frames for independent sections that update separately. -- Use Turbo Streams for real-time updates and dynamic content changes. +- For most interactions, use regular links and form submits (Turbo Drive will make them fast and dynamic) +- Decompose pages with Turbo Frames for independent sections that update separately +- Use Turbo Streams for real-time updates and dynamic content changes - Leverage Stimulus for progressive JavaScript enhancement when Turbo isn't sufficient (if Stimulus is available) -- Prefer server-side template rendering and state management over client-side frameworks. -- Enable "morphing" for seamless page updates that preserve scroll position and focus. -- Use data attributes for JavaScript hooks -- For more complex JavaScript dependencies, use Importmap Laravel +- Prefer server-side template rendering and state management over client-side frameworks and state +- Use data attributes for JavaScript hooks and CSS styling for as much as possible -## Turbo Setup & Base Helpers +## Base Helpers @verbatim - Turbo automatically handles page navigation, form submissions, and CSRF protection -- Enable morphing in your layout (preserves DOM state during page updates): `` -- Configure scroll behavior in your layout: `` -- Enable both morphing and scroll preservation with a single component: `` -- Generate unique DOM IDs from models: use function `dom_id($model, 'optional_prefix')` or Blade directive `@domid($model, 'optional_prefix')` -- Generate CSS classes from models: use function `dom_class($model, 'optional_prefix')` or Blade directive `@domclass($model, 'optional_prefix')` +- You may configure morphing and scroll preservation for a page (or layout) with: `` +- Generate unique DOM IDs from models: use the `dom_id($model, 'optional_prefix')` global function or Blade directive `@domid($model, 'optional_prefix')` +- Generate CSS classes from models: use the `dom_class($model, 'optional_prefix')` global function or Blade directive `@domclass($model, 'optional_prefix')` @endverbatim ## Turbo Frames Best Practices -- Use frames to decompose pages into independent sections that can update without full page reloads: +- Use frames to decompose pages into independent sections that can update without full page reloads +- Forms and links inside frames automatically target their containing frame (no configuration needed) +- You may override the default frame target of a link or form with `[data-turbo-frame]` attribute: + - Use a frame's DOM ID to target a specific frame + - Use the value `_top` to break out of frames and navigate the full page +- The `[:id]` prop accepts models and automatically generates DOM IDs for them +- The `[:src]` prop accepts a URL to lazy-load from content. Optionally, you may pair it with a `[loading=lazy]` so it only loads when the element is visible in the viewport + +Example: @verbatim ```blade

{{ $post->title }}

{{ $post->content }}

- Edit -
- ``` -@endverbatim -- Forms and links inside frames automatically target their containing frame (no configuration needed): -@verbatim - ```blade - + Edit
@csrf @@ -43,17 +39,10 @@ ``` @endverbatim -- Override default frame targeting with `data-turbo-frame` attribute: - - Use a frame's DOM ID to target a specific frame - - Use `_top` to break out of frames and navigate the full page: -@verbatim - ```blade - View Full Post - ``` -@endverbatim ## Turbo Streams for Dynamic Updates -- Return Turbo Stream responses from controllers to update specific page elements without full page reload: + +- You may return Turbo Streams from controllers after form submissions to update specific page elements (always check if the request accepts Turbo Streams for resilience) @verbatim public function store(Request $request) @@ -64,6 +53,11 @@ public function store(Request $request) return turbo_stream([ turbo_stream()->append('posts', view('posts.partials.post', ['post' => $post])), turbo_stream()->update('create_post', view('posts.partials.form', ['post' => new Post()])), + // turbo_stream()->prepend('some_dom_id', view('posts.partials.post', ['post' => $post])), + // turbo_stream()->before('some_dom_id', view('...')) + // turbo_stream()->after('some_dom_id', view('...')) + // turbo_stream()->replace('some_dom_id', view('...')) + // turbo_stream()->remove('some_dom_id') ]); } @@ -71,46 +65,18 @@ public function store(Request $request) } @endverbatim -- Available Turbo Stream actions for manipulating DOM elements: +- Turbo Streams can also be broadcasted using Laravel Echo for real-time updates to all users connected to a channel: @verbatim - - // Append content - turbo_stream()->append($comment, view('comments.partials.comment', [ - 'comment' => $comment, - ])); - - // Prepend content - turbo_stream()->prepend($comment, view('comments.partials.comment', [ - 'comment' => $comment, - ])); - - // Insert before - turbo_stream()->before($comment, view('comments.partials.comment', [ - 'comment' => $comment, - ])); - - // Insert after - turbo_stream()->after($comment, view('comments.partials.comment', [ - 'comment' => $comment, - ])); - - // Replace content (swaps the target element) - turbo_stream()->replace($comment, view('comments.partials.comment', [ - 'comment' => $comment, - ])); - - // Update content (keeps the target element and only updates its contents) - turbo_stream()->update($comment, view('comments.partials.comment', [ - 'comment' => $comment, - ])); - - // Removes content - turbo_stream()->remove($comment); + + -@endverbatim -- Broadcast Turbo Streams over WebSockets to push real-time updates to all connected users: -@verbatim + + // Ensure the channel is defined in `routes/channels.php`: + Broadcast::channel(Post::class, function (User $user, Post $post) { + return $user->belongsToProject($post->project); + }); + // Add the trait to the model: use HotwiredLaravel\TurboLaravel\Models\Broadcasts; @@ -120,9 +86,9 @@ class Post extends Model } // When you want to trigger the broadcasting from anywhere (including model events)... - $post->broadcastAppend()->to('posts'); $post->broadcastUpdate(); $post->broadcastRemove(); + $post->broadcastAppend()->to('posts'); @endverbatim diff --git a/src/Commands/PublishBoostGuidelineCommand.php b/src/Commands/PublishBoostGuidelineCommand.php deleted file mode 100644 index 67dfb49..0000000 --- a/src/Commands/PublishBoostGuidelineCommand.php +++ /dev/null @@ -1,23 +0,0 @@ -info('Boost guideline was published!'); - } -} diff --git a/src/TurboServiceProvider.php b/src/TurboServiceProvider.php index dd6e86e..85790ce 100644 --- a/src/TurboServiceProvider.php +++ b/src/TurboServiceProvider.php @@ -5,7 +5,6 @@ use HotwiredLaravel\TurboLaravel\Broadcasters\Broadcaster; use HotwiredLaravel\TurboLaravel\Broadcasters\LaravelBroadcaster; use HotwiredLaravel\TurboLaravel\Broadcasting\Limiter; -use HotwiredLaravel\TurboLaravel\Commands\PublishBoostGuidelineCommand; use HotwiredLaravel\TurboLaravel\Commands\TurboInstallCommand; use HotwiredLaravel\TurboLaravel\Facades\Turbo as TurboFacade; use HotwiredLaravel\TurboLaravel\Http\Middleware\TurboMiddleware; @@ -77,7 +76,6 @@ private function configurePublications(): void $this->commands([ TurboInstallCommand::class, - PublishBoostGuidelineCommand::class, ]); }