Frontend#
Waterhole's frontend is made up of Laravel Blade views and components, and uses Hotwire to coordinate partial page updates.
Blade Components#
Waterhole exposes many Blade Components that you can use in your own views.
These are documented in the
Waterhole\View\Components namespace.
Perhaps the most important of these to know is the
<x-waterhole::layout> component.
Use this to render your own views inside the Waterhole layout:
<x-waterhole::layout title="Hello World">
<h1>Hello, world!</h1>
</x-waterhole::layout>
Blade Components are only used to encapsulate more complex HTML structures and behavior. For basic UI elements like buttons and inputs, use plain HTML elements and apply Waterhole's CSS classes directly.
Component Lists#
Many parts of Waterhole's templates render lists of components. For example, the page header is made up of these components:
You can hook into these component lists and add your own components, or remove existing ones. All of these component lists are exposed as extenders.
Check out the list of extenders to learn about all the points where you can modify components. The documentation for each extender also describes what parameters will be passed into the components when they're rendered.
Adding Components#
To add a component, call the add method on the applicable component list in
your service provider:
use Waterhole\Extend;
use App\Views\Components\HelloWorld;
$this->extend(function (Extend\Ui\Layout $layout) {
$layout->header->add(HelloWorld::class);
});
The component can be any one of the following:
- The name of a Blade Component class (e.g.
HelloWorld::class) - A Blade Component instance (e.g.
new HelloWorld()) - The name of a view (e.g.
waterhole.example) - A callable that returns any of the above, which will be evaluated every time the list is rendered
Component Positions#
Each item in a component list has a "position", and components are rendered by
their position in ascending order. To specify a position, pass it as a named
argument. Otherwise, a default of 0 will be used.
use Waterhole\Extend;
$this->extend(function (Extend\Ui\Layout $layout) {
$layout->header->add(HelloWorld::class, position: 10);
});
Component Keys#
When adding a component to an extender, you can specify a unique key. This will allow other extensions to replace the component by using the same key, or remove it:
use Waterhole\Extend;
$this->extend(function (Extend\Ui\Layout $layout) {
$layout->header->add(HelloWorld::class, 'hello');
});
// Replace the existing `hello` component
$this->extend(function (Extend\Ui\Layout $layout) {
$layout->header->replace('hello', HelloWorld::class);
});
// Remove the `hello` component
$this->extend(function (Extend\Ui\Layout $layout) {
$layout->header->remove('hello');
});
Creating Component Lists#
If you're building an extension, you can expose your own extendable component
lists. Create a class extending Waterhole\Extend\Support\ComponentList and add
any default components in the constructor:
namespace Acme\Example\Extend;
use Waterhole\Extend\Support\ComponentList;
class PortalHeader extends ComponentList
{
public function __construct()
{
$this->add(PortalTitle::class, 'title', position: -10);
}
}
Rendering Component Lists#
You can retrieve the ordered component instances using the components method
(passing in any props), and then use the @components Blade directive to render
them:
@components(resolve(Acme\Example\Extend\PortalHeader::class)->components(['foo' => 'bar']))
Hotwire#
Waterhole uses Hotwire to achieve the speed and responsiveness of a single-page application, while rendering templates on the server.
The turbo-laravel library is included and can be used to help mark-up Turbo Frames and build Turbo Stream responses.
Stimulus & Custom Elements#
Waterhole's templates are all server-rendered HTML, progressively enhanced with sprinklings of JavaScript via both Stimulus and Custom Elements. Waterhole includes all the elements from the Inclusive Elements library.
Turbo Drive#
Turbo Drive accelerates links and form submissions by negating the need for full page reloads. It is enabled by default in all Waterhole views. If needed, you can opt out on individual links and forms.
Turbo Frames#
Turbo Frames allow predefined
parts of a page to be updated on request. They are used in various places
throughout the Waterhole UI. For example, a <turbo-frame> wraps each comment
so that when you click "Like" or "Edit", only that comment will be re-rendered.
You may need to be mindful of Turbo Frames when hooking into Waterhole's views. It is possible to force a link or form to "break out" of a frame if needed.
Turbo Streams#
Turbo Streams are fragments of
HTML wrapped in <turbo-stream> elements that specify where and how the HTML
should be merged into the page. Waterhole uses Turbo Streams to deliver partial
page updates after running Actions, and to broadcast real-time
updates through WebSockets.
Streamable Components#
Waterhole includes a mechanism to simplify the process of rendering a Blade
Component in a Turbo Stream. First, the component must include the
Waterhole\View\Components\Concerns\Streamable trait:
use Illuminate\View\Component;
use Waterhole\Models\Post;
use Waterhole\View\Components\Concerns\Streamable;
class PostTitle extends Component
{
use Streamable;
public function __construct(public Post $post)
{
}
public function render()
{
return <<<blade
<div {{ $attributes }}>
{{ $post->title }}
</div>
blade;
}
}
This trait exposes an id() method on the component, and the $attributes bag
will automatically be populated with an id attribute using this value. By
default, the ID will be derived from the component name and the component's
first public property that is a Model instance ($post in this example). You
can override the method if you'd like to specify a custom ID.
To render a <turbo-stream> element for the streamable component, use the
Waterhole\Views\TurboStream class. There is a method for each available Turbo
Stream action. Pass in an instance of the streamable component:
use Waterhole\Views\TurboStream;
TurboStream::replace(new PostTitle($post));
TurboStream::remove(new PostTitle($post));
TurboStream::append(new PostTitle($post), '#target_id');
TurboStream::prepend(new PostTitle($post), '#target_id');
TurboStream::before(new PostTitle($post), '#target_id');
TurboStream::after(new PostTitle($post), '#target_id');
No Results Found