diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 0000000..928cb61 --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) protonemedia + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/README.md b/README.md index 2efacc5..e0ed2fe 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,16 @@ # Laravel Splade Docs Documentation for [splade.dev](https://splade.dev) + +## Sponsor Splade + +❤️ We proudly support the community by developing Laravel packages and giving them away for free. If this package saves you time or if you're relying on it professionally, please consider [sponsoring the maintenance and development](https://github.com/sponsors/pascalbaljet). Keeping track of issues and pull requests takes time, but we're happy to help! + +## Credits + +- [Pascal Baljet](https://github.com/protonemedia) +- [All Contributors](../../contributors) + +## License + +The MIT License (MIT). Please see [License File](LICENSE.md) for more information. \ No newline at end of file diff --git a/automatic-installation.md b/automatic-installation.md index 0325d50..87b028f 100644 --- a/automatic-installation.md +++ b/automatic-installation.md @@ -1,6 +1,6 @@ # Automatic Installation -A fresh Laravel application is an ideal way to get started with Splade. The package provides a convenient Artisan command, which installs [Tailwind CSS 3.0](https://tailwindcss.com) and [Vue 3.0](https://vuejs.org) on the frontend. On the backend, it installs a Route Middleware, an Exception Handler, and it prepares a default view and with its route. +A fresh Laravel application is an ideal way to get started with Splade. The package provides a convenient Artisan command, which installs [Tailwind CSS 3.0](https://tailwindcss.com) and [Vue 3.0](https://vuejs.org) on the frontend. On the backend, it installs a Route Middleware, an Exception Handler, and it prepares a default view and its route. ```bash laravel new example-app @@ -10,8 +10,10 @@ cd example-app composer require protonemedia/laravel-splade php artisan splade:install +``` -npm install +The `splade:install` command will also build the frontend assets. Just like [regular Laravel applications](https://laravel.com/docs/10.x/vite#running-vite), you may run the Vite development server: +```bash npm run dev -``` \ No newline at end of file +```` \ No newline at end of file diff --git a/breeze.md b/breeze.md index 58c1b1f..7e4930e 100644 --- a/breeze.md +++ b/breeze.md @@ -1,6 +1,6 @@ # Breeze Starter Kit -You can use the [Laravel Breeze](https://laravel.com/docs/9.x/starter-kits#laravel-breeze) starter kit to give you a head start. Laravel Breeze is a minimal, simple implementation of all of Laravel's authentication features, including login, registration, password reset, email verification, and password confirmation. We maintain a fork with a Splade stack and keep it similar to the upstream as much as possible. +You can use the [Laravel Breeze](https://laravel.com/docs/10.x/starter-kits#laravel-breeze) starter kit to give you a head start. Laravel Breeze is a minimal, simple implementation of all of Laravel's authentication features, including login, registration, password reset, email verification, and password confirmation. We maintain a fork with a Splade stack and keep it similar to the upstream as much as possible. The installation process is similar to the [Automatic Installation](/automatic-installation.md). @@ -12,4 +12,10 @@ cd example-app composer require protonemedia/laravel-splade-breeze php artisan breeze:install -``` \ No newline at end of file +``` + +The `breeze:install` command will also build the frontend assets. Just like [regular Laravel applications](https://laravel.com/docs/10.x/vite#running-vite), you may run the Vite development server: + +```bash +npm run dev +```` \ No newline at end of file diff --git a/bridge-components.md b/bridge-components.md new file mode 100644 index 0000000..949ee17 --- /dev/null +++ b/bridge-components.md @@ -0,0 +1,220 @@ +# Bridge Components (in beta) + +As of version 1.3, Splade supports bridging Blade Components and Vue Templates. This means there's two-way binding of the public PHP properties and the templates' properties, and you may call public methods from within the template as if they were JavaScript methods. + +> **Warning** +> This feature is still in beta and therefore experimental. Use with care! + +## Prerequirement + +First, you must register a supporting route using the `spladeWithVueBridge()` method on the `Route` facade. As of version 1.3.0, the automatic installer does this for you. If you need to register the route manually, make sure it uses the `web` Middleware, for example, in `web.php`: + +```php +Route::spladeWithVueBridge(); +``` + +## Example component + +Let's start with an example component, and later on, we'll dive into the technical details. We'll start with creating a component to quickly fake a user's email address: + +```bash +php artisan make:component FakeEmail +``` + +The component has two properties: a User model and an optional prefix. There's a method that performs the update, and lastly, there is the `WithVue` trait: + +```php +user->update([ + 'email' => $this->prefix . fake()->email, + ]); + } + + public function render() + { + return view('components.fake-email'); + } +} +``` + +In the template, all public properties of the Blade Component are available through the `props` object. So instead of echoing the email address using Blade's curly braces, we'll use Vue's syntax to ensure the two-way binding. Finally, we'll attach the method to the button's click handler and use the `v-model` directive to set the prefix: + +```blade +

User Email:

+ + + + +``` + +That's it! You don't have to register a route or controller for each new component. Instead, everything is handled for you using the `WithVue` trait. Now you may use the Blade Component: + +```blade + +``` + +## Security + +Please be very aware that all public properties are exposed to the browser and thus visible to the end user. Be sure sensitive Model attributes are [hidden](https://laravel.com/docs/10.x/eloquent-serialization#hiding-attributes-from-json), or use the built-in [Transformer feature](/transformers.md). + +## Collections + +Similar to not using Blade's curly braces, you should use Vue's [`v-for` directive](https://vuejs.org/guide/essentials/list.html) to loop over data instead of Blade's `@foreach` directive. This way, the list will reflect updates that may happen server-side. + +```blade + +``` + +## Renderless example + +Instead of using the view template, you may also omit the `render` method and use the component directly in your template. The great thing about that is that you may reuse the same logic repeatedly but still be able to fully customize the UI. So first, let's remove the method: + +```php +user->update([ + 'email' => $this->prefix . fake()->email, + ]); + } +} +``` + +Now you can use the component in other templates and provide the UI with a slot: + +```blade +

Page Title

+ + +

User Email:

+ + + + +
+``` + +## Inline Component View + +Instead of using a dedicated file for the template, you may also use an [Inline Component View](https://laravel.com/docs/10.x/blade#inline-component-views): + +```php +ip = gethostbyname($this->hostname); + } + + public function render() + { + return <<<'blade' + + +

+ blade; + } +} +``` + +## Toasts and Redirects + +You may send a [Toast](/toasts.md) from a component method to the frontend: + +```php +use ProtoneMedia\Splade\Facades\Toast; + +public function fake() +{ + $this->user->update([ + 'email' => $this->prefix . fake()->email, + ]); + + Toast::success('User updated'); +} +``` + +Similarly, you may return a Redirect: + +```php +public function fake() +{ + $this->user->update([ + 'email' => $this->prefix . fake()->email, + ]); + + return redirect()->route('users.index'); +} +``` + +## Middleware and Rate Limiting + +By default, component methods called from the frontend will use the same Middleware stack as the original route. In addition, you may call the `middleware` method to apply additional constraints: + +```php +public function fake() +{ + $this->middleware('can:update-email'); + + $this->user->update([ + 'email' => $this->prefix . fake()->email, + ]); +} +``` + +Also, just like controller methods, public component methods may be manually invoked by users. Therefore, always validate incoming data and, when necessary, use a [Rate Limiter](https://laravel.com/docs/10.x/rate-limiting#main-content). \ No newline at end of file diff --git a/credits.md b/credits.md index f2f31d2..4ed02e5 100644 --- a/credits.md +++ b/credits.md @@ -6,4 +6,4 @@ I'm a *massive* fan of [Inertia.js](https://inertiajs.com), and tons of Splade f Then I'd like to thank [Caleb](https://twitter.com/calebporzio) for his work on [Alpine.js](https://alpinejs.dev), a modern, minimal JavaScript framework. You'll also find that some patterns were hugely inspired by it. Two other projects I should mention are [Wire Elements](https://wire-elements.dev) by [Philo](https://twitter.com/Philo01) and [Momentum Modal](https://github.com/lepikhinb/momentum-modal) by [Boris](https://twitter.com/lepikhinb/). Both were tremendously helpful while implementing the Modal feature in Splade. -And lastly, I need to thank [Taylor](https://twitter.com/taylorotwell) and the rest of the [Laravel team](https://laravel.com/team) for bringing us the best web application framework we've ever seen. To make Splade look familiar to developers, I've based most of the frontend-boilerplate on [Jetsteam](https://jetstream.laravel.com/2.x/introduction.html). +And lastly, I need to thank [Taylor](https://twitter.com/taylorotwell) and the rest of the [Laravel team](https://laravel.com/team) for bringing us the best web application framework we've ever seen. To make Splade look familiar to developers, I've based most of the frontend-boilerplate on [Jetstream](https://jetstream.laravel.com/2.x/introduction.html). diff --git a/custom-form-components.md b/custom-form-components.md new file mode 100644 index 0000000..4e9e987 --- /dev/null +++ b/custom-form-components.md @@ -0,0 +1,340 @@ +# Custom Form components + +Splade comes with a great set of [built-in Form Components](/form-overview.md), but your application might require a custom component as part of a form. There are several ways to implement this, so we’ll look at implementing the same custom component differently. + +## Card Select component + +The component we'll be implementing is a *card selector*. In this example, two cards represent a subscription plan (*basic* or *pro*). Clicking on one of the cards will update the form data, as the cards are built around a `radio` input element. Here's what it looks like: + + + +## Blade implementation + +First, let's take a look at a Blade implementation. This implementation works without any custom Vue components. For the sake of simplicity, we'll hard-code both plans, and we don't extract each card into a separate component. + +```blade + +

+ + {{-- Plan Basic --}} + + + {{-- Plan Pro --}} + + +
+ +``` + +Let's break down what's happening here. First, there's a [Form component](/x-form.md) with a default *basic* plan. + +```blade + +``` + +Then there are two Cards built around a `label` element. The border color is based on whether the plan is selected or not. + +```blade + +``` ## Confirmation @@ -83,6 +105,34 @@ In addition, you may customize the confirmation dialog: > ``` +Instead of the `confirm` attribute, you may also use the `confirm-danger` attribute to render a red confirmation button. + +### Password Confirmation + +It's even possible to require the user to confirm their password within the confirmation dialog. First, you must register a supporting route using the `spladePasswordConfirmation()` method on the `Route` facade. As of version 1.2.2, the automatic installer does this for you. If you need to register the route manually, make sure it uses the `web` Middleware, for example, in `web.php`: + +```php +Route::spladePasswordConfirmation(); +``` + +Now you may add the `require-password` attribute: + +```blade + +``` + +Only when the password is correct it submits the form. Splade will add the entered password to the form data as a `password` field. This way, you may recheck the password *server-side* in the form target action. For security reasons, this is disabled for `GET` requests. You can change the field by passing a value to the `require-password` attribute: + +```blade + +``` + +To prevent users from re-entering their password over and over, you may use the `require-password-once` attribute. Once the user has successfully entered their password, it won't be asked again until the number of seconds (defined with the `auth.password_timeout` config key) has elapsed: + +```blade + +``` + ## File uploads You may use the input event to bind the selected file to your form data: @@ -97,6 +147,71 @@ You may use the input event to bind the selected file to your form data: The dedicated [File Component](/form-file.md) provides a cleaner solution, and has support for selecting multiple files as well as displaying the filename of the selected file. +## File downloads + +If the form submittion results in a file download, you may use the `blob` attribute to let Splade handle the download: + +```blade + + + + +``` + +## State + +There are several props that you can use to show the state of the form: + +```blade + +

Submitting the data...

+
+ + +

Successfully submitted!

+ +

Flash message to show success!

+
+``` + +## Submit on change + +Sometimes you want to submit the form whenever a value changes, for example, on a settings page that you want to save immediately. For this, you may use the `submit-on-change` attribute. + +```blade + +``` + +In addition, you may optionally specify one or more values (with an `array` or `string`) that Splade should watch instead of all values. + +```blade + + + + + +``` + +While this is great for elements like checkboxes, radios, and selects, it might lead to a bad user experience when applied to text inputs, as the form disables user input on submission. To overcome this, you may use the `background` attribute. This prevents to form from disabling user input and leaves the state properties (`processing`, `wasSuccessful`, and `recentlySuccessful`) untouched. In addition, to prevent the form from submitting on every keystroke, you may use the `debounce` attribute to set a debounce time in milliseconds: + +```blade + + + +``` + +If you still want to indicate whether the form is processing, you may use the `processingInBackground` property: + +```blade + + + +

Saving message in background...

+ + +
+``` + ## Reset and restore form You may use the `form.reset` method to clear all form data. @@ -119,9 +234,31 @@ Similarly, there's a `form.restore` method to restore the default values.
``` +### Prevent navigation on submit + +If you want to stay on the same page after a successful request and want to preserve the page's current state, add the `stay` attribute. + +```blade + +``` + +Additionally, a `preserve-scroll` attribute prevents the page from scrolling to the top. This can be useful when submitting a form from a [Table component](/table-overview.md) while returning `redirect()->back()` from the controller: + +```blade + +``` + +When the form has been submitted successfully, you may access the response using the `form.$response` prop: + +```blade + +
+
+```
+
 ### Reset and restore on success
 
-If you redirect back to the same page after a successful request and want to preserve the page's current state, add the `stay` attribute. You may choose to reset or restore the form data automatically. You can do this with the `reset-on-success` and `restore-on-success` attributes:
+You may choose to reset or restore the form data automatically. You can do this with the `reset-on-success` and `restore-on-success` attributes:
 
 ```blade
 
@@ -129,22 +266,40 @@ If you redirect back to the same page after a successful request and want to pre
 
 ```
 
-## State
+## Form API
 
-There are several props that you can use to show the state of the form:
+The `form` object has several additional methods and properties that you could use. With the `$put` method, you can set a value:
 
 ```blade
-
-    

Submitting the data...

-
+ +``` - -

Successfully submitted!

+The `$all` property could help you debug the Form by printing all values: -

Flash message to show success!

-
+```blade +
+```
+
+If you'd somehow need to submit the Form with a custom trigger, you can use the `submit` method:
+
+```blade
+
Start
+``` + +As shown above, there's an `errors` object to evaluate validation errors, but there's also a `hasError` method to determine whether there is an error: + +```blade +... +``` + +If you want full access to the server-side error bag, you may use the `rawErrors` object. + +```blade +
+ ... +
``` ## Form components -While writing traditional input elements is fine, Splade comes with various components to build forms even faster. Make sure to check out the [documentation page](/form-overview.md)! \ No newline at end of file +While writing traditional input elements is fine, Splade comes with various components to build forms even faster. Make sure to check out the [documentation page](/form-overview.md). There's also an extensive guide about creating [custom form components](/custom-form-components.md). diff --git a/x-link.md b/x-link.md index 442af4d..7efba70 100644 --- a/x-link.md +++ b/x-link.md @@ -27,6 +27,16 @@ In addition, you may customize the confirmation dialog: ``` +Instead of the `confirm` attribute, you may also use the `confirm-danger` attribute to render a red confirmation button. + +## Redirecting To External Domains + +When the URL is outside your application pointing to an external domain, you'd typically use a regular `` element. Still, you may use the `away` attribute on the `Link` component. This can be useful when you have wrapped the component into another component and don't want to change the tag dynamically. + +```blade +Google +``` + ## Transform all anchors If you don't want to use the `Link` component but want Splade to transform all `` elements, you need to update the plugin options in the `app.js` file: @@ -37,4 +47,40 @@ createApp({ render: renderSpladeApp({ el }) }) 'transform_anchors': true, }) .mount(el); +``` + +## Method, Headers, and Request Data + +By default, the asynchronous page load is a `GET` request, but you may change this with the `method` attribute: + +```blade +Start new template +``` + +The component also supports custom headers and request data. While you can use the `headers` and `data` attributes on the `Link` component, there's also a Blade variant of the component. Just like the [Data component](/x-data.md), it allows you to pass a PHP value *or* a JavaScript object: + +The value passed to the `data` attribute will be parsed by Vue, not by PHP. + +```blade + +``` + +If you want to parse the value by PHP, you may use the `:data` attribute (note the colon). + +```blade + +``` + +You can do the same for adding headers: + +```blade + + + +``` + +Additionally, a `preserve-scroll` attribute prevents the page from scrolling to the top. This can be useful when submitting a form from a [Table component](/table-overview.md) while returning `redirect()->back()` from the controller: + +```blade + ``` \ No newline at end of file diff --git a/x-modal.md b/x-modal.md index e7e0997..66dbd2a 100644 --- a/x-modal.md +++ b/x-modal.md @@ -1,8 +1,8 @@ # X-Splade-Modal Component -With the **Modal Component**, Splade has built-in support for modals and slideover. This component allows you to load *any* route into a modal. To prepare a view so you can use it inside a modal, you have to wrap the content in a `` component. Nothing changes when requesting the view *outside* of the modal. Everything will work as it used to be. +With the **Modal Component**, Splade has built-in support for modals and slideover. This component allows you to load *any* route into a modal. Besides loading the content asynchronously, it also supports pre-loaded content. -For example, here's a page to create a new user. This page is a regular, full-page view that extends the base layout. +First, let's take a look at loading a route into a modal. To prepare a view to use it inside a modal, you have to wrap the content in a `` component. Nothing changes when requesting the view *outside* of the modal. Everything will work as it used to. For example, here's a page to create a new user. This page is a regular, full-page view that extends the base layout. ```blade @extends('layout') @@ -60,6 +60,34 @@ You can control the size of the modal with the `max-width` attribute. Valid valu ``` +## Position + +By default, the modal is vertically centered. However, you may customize this with the `position` attribute, which you can set to either `top`, `center`, or `bottom`: + +```blade + +``` + +The slideover opens on the right side by default, but you may change that as well with the `position` attribute: + +```blade + +``` + +## Close behavior + +By default, the modal closes when the user presses the *escape* key or clicks outside of the modal. However, you may disable this with the `close-explicitly` attribute: + +```blade + +``` + +Suppose you want this to be the default behavior. In that case, you may call the `defaultModalCloseExplicitly` method on the `Splade` facade, for example, in the `AppServiceProvider` class: + +```php +Splade::defaultModalCloseExplicitly(); +``` + ## Close Button The modal and slideover have a *close button*, which you can disable with the `close-button` attribute. @@ -79,3 +107,40 @@ You can manually close the modal or slideover with the `modal.close()` or `modal ``` +## Pre-loaded content + +Instead of loading the content asynchronously, you may also pass the content along with the page. You may do this by passing a `name` attribute to the component and use the name along with a `#` prefix in the `Link` component: + +```blade +

Tickets & Tour Dates

+ + + Show Refund Information + + + +

...

+
+``` + +To use a slideover, you must put the attribute on the `x-splade-component` instead of the `Link` component. The `max-width` attribute works with pre-loaded content as well. + +```blade +

Tickets & Tour Dates

+ + + Show Refund Information + + + +

...

+
+``` + +Additionally, it's possible to immediately show the modal on page load with the `opened` attribute: + +```blade + +
secret-token
+
+``` \ No newline at end of file diff --git a/x-rehydrate.md b/x-rehydrate.md new file mode 100644 index 0000000..f513f46 --- /dev/null +++ b/x-rehydrate.md @@ -0,0 +1,76 @@ +# X-Splade-Rehydrate Component + +The **Rehydrate Component** can reload a section of your Blade template. You may combine this component with the [Event Bus](/event-bus.md) to trigger the reload on certain events. + +To get started, wrap the section you want to be *reloadable* in the component. For example, imagine a list of team members: + +```blade +
    + @foreach($team->members as $member) +
  • {{ $member->name }}
  • + @endforeach +
+``` + +You may wrap the list and pass the name of the event to the `on` attribute: + +```blade + +
    + @foreach($team->members as $member) +
  • {{ $member->name }}
  • + @endforeach +
+
+``` + +If there's a form that stays on this page after a successful request, you may emit an event that reloads the list: + +```blade + + + + +``` + +Of course, when the form doesn't have the `stay` attribute and redirects back to the same page, it will reload the complete page, including the list, but you'll lose the state of other components on the page. This pattern can help avoid that. + +### Listen to multiple events + +You may listen to multiple events with an array or string: + +```blade + + + +``` + +## Poll + +You may also use this component to poll for new data. With the `poll` attribute, you can specify the interval in milliseconds. + +```blade + + Today's score: {{ $score }} + +``` + +## Conditionals + +Don't wrap the Rehydrate Component into an *if-statement*, but always put the statement *within* the component: + +```blade + +@if($user->canUpdateTeam()) + + ... + +@endif + + + + @if($user->canUpdateTeam()) + ... + @endif + +``` \ No newline at end of file diff --git a/x-script.md b/x-script.md new file mode 100644 index 0000000..b7d5508 --- /dev/null +++ b/x-script.md @@ -0,0 +1,20 @@ +# X-Splade-Script Component + +In addition to building [custom Vue components](/custom-vue-components.md), you may use the **Script Component** to add inline scripts to your Blade template. Usually, the Vue render engine would throw an error on script tags, but this component magically injects your script as if it was called in the [`mounted()`](https://vuejs.org/api/options-lifecycle.html#mounted) hook of a Vue component. This way, we can avoid the typical error, and you get access to the global `$splade` object, which you may use to navigate and access the Event Bus. + +Note that this component is quite limited, and it is generally recommended to use custom Vue components. + +```blade + + document.body.classList.add('bg-confetti'); + +``` + +Here's another example of using Splade's navigation capabilities: + +```blade + + setTimeout(() => $splade.visit('/destination'), 5000); + +``` + diff --git a/x-transition.md b/x-transition.md index 8fa44e3..4b7edf1 100644 --- a/x-transition.md +++ b/x-transition.md @@ -62,4 +62,4 @@ Now you can use the preset in your template: ## Included animations -The included animations are `default`, `opacity`, `fade`, and `slide-right`. \ No newline at end of file +The included animations are `default`, `opacity`, `fade`, `slide-left`, and `slide-right`. \ No newline at end of file