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:
+
+
+
+Fake 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:
+
+
+
+ Fake 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'
+
+ Find IP
+
+ 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
+
+
+
+```
+
+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
+
+```
+
+Within the `label` element, there's an `input` element. It has the `value` set to the plan and two-way binding by setting the `v-model` to `form.plan`.
+
+```blade
+
+```
+
+Lastly, an `SVG` element (checkmark icon) that's only visible when the plan is selected.
+
+```blade
+
+
+
+```
+
+### Extract Card into Blade Component
+
+We begin by creating a new Blade Component. As this is a fairly simple component, we'll create an anonymous component with only a view:
+
+```bash
+php artisan make:component CardLabel --view
+```
+
+Then, we can move the Card component out of the form and paste it into the new Blade component view. We'll replace all hardcoded values with variables. To pass PHP values to Vue, always use Laravel's `@js` directive. Note how the `radio` input element uses a `:value` attribute (with a colon).
+
+```blade
+
+
+
+
+
+ $ {{ $price }} / month
+
+```
+
+Lastly, we'll update the form to use the new component:
+
+```blade
+
+
+
+
+
+
+```
+
+## Vue implementation
+
+Instead of building the Card component with a Blade template, you could also create a custom Vue component. Again, there are many ways of doing this, but let's show two examples corresponding to the Blade examples.
+
+Matching the first Blade example, you could create a Vue component that contains both plans. We'll create a `PlanSelector.vue` component and pass the `form` object as a property. By passing the entire `form` object to the component, you can interact with it *within* the Vue component by using the [Form API](/x-form.md). Remember to [register the custom Vue component](/custom-vue-components.md) in the main `app.js` file.
+
+```vue
+
+
+
+
+
+```
+
+Then in the Blade template, you'd use this component and pass the `form` object:
+
+```blade
+
+
+
+```
+
+### Replace Form object with v-model
+
+Instead of passing the entire `form` object to the Vue component, you could also use a `v-model` directive:
+
+```blade
+
+
+
+```
+
+Then we'd need to update the Vue component to work as a [custom input](https://vuejs.org/guide/components/events.html#usage-with-v-model). We'll replace `form.plan` with `modelValue`, and replace the `v-model` directive on the input element with `:checked` and `@change` attributes.
+
+```vue
+
+
+
+
+
+```
+
+Again, this works fine, but you don't want to hardcode the plans into the Vue component.
+
+### Extract Card into Vue Component
+
+Let's take the Vue implementation one step further, and extract the Cards into a dedicated Vue component. We'll name this component `CardLabel.vue`. As the plans are now dynamic, we'll add a `plan`, `price`, and `title` property:
+
+```vue
+
+
+
+
+
+
+
+ $ {{ price }} / month
+
+
+
+
+```
+
+You can use the new `CardLabel` component in the Blade template:
+
+```blade
+
+
+
+
+
+
+```
+
+## Renderless Vue component
+
+Vue also supports renderless components, which don't include a *template* section. So in the example below, the `total` value will be updated whenever the `price` or `amount` value change.
+
+```blade
+
+
+
+
+
+
+
+```
+
+We'll pass the `form` object to the Vue component again and then set up watchers for the `price` and `amount` values, which will call the `updateTotal` method:
+
+```vue
+
+```
\ No newline at end of file
diff --git a/custom-vue-components.md b/custom-vue-components.md
index d81aa53..0e63975 100644
--- a/custom-vue-components.md
+++ b/custom-vue-components.md
@@ -60,6 +60,29 @@ Now you may use the component in a Blade template:
```
+## Async Components
+
+When you register components in the main `app.js`, like in the example above, the components will end up in the `app.js` bundle when you build the assets for production use. You may also use *dynamic imports*, which will only load a component from the server when needed. Vue has a [built-in](https://vuejs.org/guide/components/async.html) `defineAsyncComponent` function to accomplish this.
+
+```js
+import { createApp } from "vue/dist/vue.esm-bundler.js"; // [tl! remove]
+import { createApp, defineAsyncComponent } from "vue/dist/vue.esm-bundler.js"; // [tl! add]
+
+import Counter from "./Components/Counter.vue"; // [tl! remove]
+
+createApp({
+ render: renderSpladeApp({ el })
+})
+ .use(SpladePlugin, {
+ "max_keep_alive": 10,
+ "transform_anchors": false,
+ "progress_bar": true
+ })
+ .component('Counter', Counter) // [tl! remove]
+ .component('Counter', defineAsyncComponent(() => import("./Components/Counter.vue"))) // [tl! add]
+ .mount(el);
+```
+
## Passing data
If you need to pass data to a Vue component (as a property), you may use the `@js` directive:
@@ -139,7 +162,7 @@ Now you can use the renderless Vue component in a Blade template, and use the `i
```
-Note that you can't use the `{{ counter.count }}` Vue syntax to echo out the count value, as the template is rendered first by the Blade engine, and Blade [uses the same syntax](https://laravel.com/docs/9.x/blade#blade-and-javascript-frameworks). You may use the `@` symbol to inform Blade the expression should remain untouched. This would result in `@{{ counter.count }}`. Though this works fine, using the `v-text` attribute seems neater.
+Note that you can't use the `{{ counter.count }}` Vue syntax to echo out the count value, as the template is rendered first by the Blade engine, and Blade [uses the same syntax](https://laravel.com/docs/10.x/blade#blade-and-javascript-frameworks). You may use the `@` symbol to inform Blade the expression should remain untouched. This would result in `@{{ counter.count }}`. Though this works fine, using the `v-text` attribute seems neater.
You may also use the *destructuring assignment syntax* in the slot:
diff --git a/customization.md b/customization.md
index aa78577..075ee7e 100644
--- a/customization.md
+++ b/customization.md
@@ -3,7 +3,15 @@
You can find every bit of styling in Blade templates, so you never have to customize Vue components. You may publish the Blade templates using the `vendor:publish` Artisan command:
```bash
-php artisan vendor:publish --provider="ProtoneMedia\Splade\ServiceProvider"
+php artisan vendor:publish --provider="ProtoneMedia\Splade\ServiceProvider" --tag="views"
```
-Now the templates are available in the `/resources/views/vendor/splade` folder.
\ No newline at end of file
+Now the templates are available in the `/resources/views/vendor/splade` folder. Only the styled templates are published. The strictly functional components (those without any markup or styling) will not be published, as there's nothing to customize.
+
+## Laravel Configuration File (config/splade.php)
+
+You may also publish the configuration file. This allows you to configure settings like the Blade Component prefix, SEO defaults, SSR settings, and more.
+
+```bash
+php artisan vendor:publish --provider="ProtoneMedia\Splade\ServiceProvider" --tag="config"
+```
\ No newline at end of file
diff --git a/event-bus.md b/event-bus.md
new file mode 100644
index 0000000..78c1d35
--- /dev/null
+++ b/event-bus.md
@@ -0,0 +1,67 @@
+# Event Bus
+
+Splade has a built-in **Event Bus** that allows for communication between components. Events can be emitted by one component, and listened for by another.
+
+## Emit from Vue component
+
+To emit an event, you may call the `emit` method, for example, in a [custom Vue component](/custom-vue-components.md), on the global `$splade` instance:
+
+```vue
+
+```
+
+If you're using the `setup` attribute on a `
+```
+
+Additionally, you may pass data along with the event:
+
+```js
+Splade.emit('checkout', { id: 1 });
+```
+
+## Emit on event
+
+It's also possible to call the `emit` method from another event. For example, [Bridge Components](/bridge-components.md) and the [Form Component](/x-form.md) emit `error` and `success` events that you can hook into:
+
+```blade
+
+```
+
+## Listen for events
+
+You may register a listener by using the `on` method:
+
+```js
+this.$splade.on('checkout-failed', function() {
+ // do something
+});
+```
+
+When the callback is rather simple, you may use a [Script Component](/x-script.md) to avoid writing a custom Vue component:
+
+```blade
+
+ $splade.on('checkout-failed', function() {
+ // do something
+ });
+
+```
\ No newline at end of file
diff --git a/exception-handling.md b/exception-handling.md
new file mode 100644
index 0000000..22c1ea8
--- /dev/null
+++ b/exception-handling.md
@@ -0,0 +1,41 @@
+# Exception handling
+
+## Exceptions in PHP
+
+Splade uses a custom Exception Handler, registered in the `Exceptions/Handler.php` file by the [automatic installer](/automatic-installation.md).
+
+```php
+use ProtoneMedia\Splade\SpladeCore;
+
+class Handler extends ExceptionHandler
+{
+ public function register()
+ {
+ $this->renderable(SpladeCore::exceptionHandler($this));
+ }
+}
+```
+
+If you want to add a custom handler, you may pass a second argument with a closure to the `exceptionHandler()` method. This implementation is similar to Laravel's [default implementation](https://laravel.com/docs/10.x/errors#rendering-exceptions).
+
+```php
+SpladeCore::exceptionHandler($this, function (Throwable $e, Request $request) {
+ if ($e instanceof HttpException && $e->getStatusCode() === 419) {
+ return new RedirectResponse('/login?reason=timeout');
+ }
+});
+```
+
+## Exceptions in the browser (JavaScript/Vue)
+
+As Vue renders the templates in the browser, invalid markup might cause an error. The most common error is a missing HTML element, like a closing `` tag. Unfortunately, Vue handles such errors differently in development and production mode. In development mode, Vue will throw an error in the console, but in production mode, Vue will silently fail and most likely render a blank page.
+
+Splade can mimic the development behavior in production mode to prevent rendering a blank page. Note that this will not fix the underlying issue, but it will at least show an error message in the browser console instead of showing a blank page. To enable this behavior, you need to update the plugin options in the main `app.js` file by setting the `suppress_compile_errors` option to `true`:
+
+```js
+createApp({ render: renderSpladeApp({ el }) })
+ .use(SpladePlugin, {
+ 'suppress_compile_errors': true,
+ })
+ .mount(el);
+```
\ No newline at end of file
diff --git a/form-builder-fields.md b/form-builder-fields.md
new file mode 100644
index 0000000..4f2577c
--- /dev/null
+++ b/form-builder-fields.md
@@ -0,0 +1,283 @@
+# Form Builder Fields
+
+Here's an overview of all fields that are available in the [Form Builder](/form-builder-overview.md).
+
+## Text Input Field
+
+Renders an [Input Component](/form-input.md).
+
+```php
+Input::make('name')
+ ->label('Enter your name')
+ ->rules('required', 'max:255')
+ ->help('I need somebody!')
+ ->placeholder('John Doe')
+ ->minLength(2)
+ ->maxLength(255);
+
+// or:
+
+Text::make('name');
+```
+
+You may also define the validation rules as an array or a piped string:
+
+```php
+Input::make('text')
+ ->rules(['required', 'max:255'])
+ ->rules('required|max:255');
+```
+
+There are some additional methods that you may call on the field:
+
+```php
+Input::make('text')
+ ->append('@splade.dev') // Appends a text to the field
+ ->prepend('www.') // Prepends a text to the field
+ ->placeholder('Text') // Placeholder value for the field
+ ->disabled() // Disables an input field
+ ->readonly() // Makes an input read-only
+ ->class('w-1/2') // Add additional classes to the field
+
+ ->attributes(['data-custom' => 'foo']); // Add additional attributes
+```
+
+### Hidden Field
+
+```php
+Hidden::make('hidden_field');
+
+// or:
+
+Input::make('hidden_field')->hidden();
+```
+
+### Number
+
+```php
+Number::make('number')
+ ->label('Number input')
+ ->minValue(1)
+ ->maxValue(100)
+ ->unsigned(), // alias for ->minValue(0)
+ ->step(0.01);
+```
+
+### Email
+
+```php
+Email::make('email');
+
+// or:
+
+Input::make('email')->email();
+```
+
+### Passwords
+
+```php
+Password::make('password');
+
+// or:
+
+Input::make('password')->password();
+```
+
+## Colorpicker
+
+```php
+Color::make('color');
+
+// or:
+
+Input::make('color')->color();
+```
+
+## Textarea
+
+Renders a [Textarea Component](/form-textarea.md).
+
+```php
+Textarea::make('textarea')->autosize();
+```
+
+## Date and Time
+
+Renders an [Input Component](/form-input.md) with Flatpickr.js integation.
+
+### Date
+
+```php
+Date::make('date');
+
+Date::make('date')->date(['showMonths' => 2]); // Flatpickr options
+```
+
+### Time
+
+```php
+Time::make('time');
+
+Time::make('time')->time(['time_24hr' => false]); // Flatpickr options
+```
+
+### Datetime
+
+```php
+Datetime::make('datetime');
+```
+
+## Files
+
+Renders an [File Component](/form-file.md) with optional Filepond integation.
+
+```php
+File::make('photo')
+ ->multiple() // Enables selecting multiple files
+ ->filepond() // Enables filepond
+ ->accept('image/jpeg')
+ ->accept(['image/png', 'image/jpeg']);
+```
+
+Additional Filepond configuration:
+
+```php
+File::make('photo')
+ ->filepond()
+ ->server() // Enables asynchronous uploads of files
+ ->preview() // Show image preview
+
+ ->minSize('1Mb')
+ ->maxSize('10Mb')
+
+ ->width(120)
+ ->height(120)
+
+ ->minWidth(150)
+ ->maxWidth(500)
+
+ ->minHeight(150)
+ ->maxHeight(500)
+
+ ->minResolution(150)
+ ->maxResolution(99999)
+```
+
+## Checkboxes
+
+Renders a [Checkbox Component](/form-checkbox.md).
+
+Checkboxes can be defined as separate fields:
+
+```php
+Checkbox::make('options[]')->label('Checkbox 1')->value('checkbox-1');
+Checkbox::make('options[]')->label('Checkbox 2')->value('checkbox-2');
+```
+
+Or as an options array:
+
+```php
+Checkboxes::make('options')
+ ->label('Choose your options')
+ ->options([
+ 'option-1' => 'Option 1',
+ 'option-2' => 'Option 2',
+ 'option-3' => 'Option 3',
+ ])
+ ->inline(); // inline is optional
+```
+
+## Radios
+
+Renders a [Radio Component](/form-radio.md).
+
+Radios can be defined as separate inputs:
+
+```php
+Radio::make('option')->label('Radio 1')->value('radio-1'),
+Radio::make('option')->label('Radio 2')->value('radio-2'),
+Radio::make('option')->label('Radio 3')->value('radio-3')->help('I need somebody!'),
+```
+
+Or as an array:
+
+```php
+Radios::make('theme')
+ ->label('Choose a theme')
+ ->options([
+ 'dark' => 'Dark theme',
+ 'light' => 'Light theme',
+ 'system' => 'System theme',
+ ])
+ ->inline(); // inline is optional
+```
+
+## Selects
+
+Renders a [Select Component](/form-select.md).
+
+```php
+$options = [
+ 'be' => 'Belgium',
+ 'de' => 'Germany',
+ 'fr' => 'France',
+ 'lu' => 'Luxembourg',
+ 'nl' => 'The Netherlands',
+];
+
+Select::make('country')
+ ->label('Choose a country')
+ ->options($options);
+
+Select::make('countries[]')
+ ->label('Choose multiple countries')
+ ->options($options)
+ ->multiple() // Enables choosing multiple options
+ ->choices(); // Enables the Choices.js integration
+
+Select::make('countries[]')
+ ->options($options)
+ ->multiple()
+ ->choices(['searchEnabled' => false]); // Pass Choices.js options
+
+Select::make('user')
+ ->label('Select with data from a remote URL')
+ ->remoteUrl('/api/users');
+
+Select::make('user')
+ ->label('Select with data from a remote URL with a remote root')
+ ->remoteUrl('/api/users')
+ ->remoteRoot('data.users')
+ ->optionLabel('name')
+ ->optionValue('id');
+
+Select::make('user')
+ ->label('Select with Option Label and Value')
+ ->options([
+ ['user_id' => 10, 'user_name' => 'John'],
+ ['user_id' => 20, 'user_name' => 'Jane'],
+ ['user_id' => 30, 'user_name' => 'Mary'],
+ ['user_id' => 40, 'user_name' => 'Peter'],
+ ])
+ ->optionLabel('user_name')
+ ->optionValue('user_id');
+```
+
+## Submit
+
+Renders a [Submit Component](/form-submit.md).
+
+```php
+Submit::make()->label('Send')
+```
+
+### Button
+
+```php
+Button::make('close_button')
+ ->label('Close Modal')
+ ->attributes(['@click.prevent' => 'form.restore'])
+ ->danger() // optional
+ ->secondary(); // optional
+```
+
diff --git a/form-builder-overview.md b/form-builder-overview.md
new file mode 100644
index 0000000..c884099
--- /dev/null
+++ b/form-builder-overview.md
@@ -0,0 +1,303 @@
+---
+description: Splade has an advanced Form Builder that enables you to build forms from your controllers instead of in your templates.
+keywords: laravel form, laravel forms, laravel formbuilder
+---
+
+# Form Builder
+
+Splade has an advanced Form Builder that enables you to build forms from your controllers instead of in your templates. Check out the [Form Components](/form-overview.md) section to learn more about Splade's form capabilities.
+
+## Basic example
+
+You may use the `SpladeForm` class to configure the form.
+
+```php
+action(route('users.store'))
+ ->fields([
+ Input::make('name')->label('User Name'),
+ Password::make('password')->label('Password'),
+ Submit::make()->label('Create'),
+ ]);
+
+ return view('users.create', [
+ 'form' => $form,
+ ]);
+ }
+}
+```
+
+Then in the Blade template, you must pass the form to the `for` attribute. That's it!
+
+```blade
+
+```
+
+Besides the `action()` and `fields()` methods, there are additional methods to configure the form:
+
+```php
+SpladeForm::make()
+ ->id('create-user-form')
+ ->class('space-y-4')
+ ->method('POST');
+```
+
+To show a confirmation dialog before Splade submits the form, simply use the `confirm()` method. You may customize the confirmation dialog as well.
+
+```php
+SpladeForm::make()->confirm();
+
+SpladeForm::make()->confirm(
+ confirm: 'Delete user',
+ text: 'Are you sure you want to delete this user?',
+ confirmButton: 'Delete',
+ cancelButton: 'Cancel',
+ danger: true,
+ requirePassword: true,
+);
+```
+
+Instead of requiring a password using the `confirm()` method, you may also use the dedicated `requirePassword()` method:
+
+```php
+SpladeForm::make()->requirePassword();
+
+SpladeForm::make()->requirePassword(
+ requirePasswordOnce: false,
+ heading: 'Please enter your password',
+ text: 'Please confirm your password before continuing',
+ confirmButton: 'Confirm',
+ cancelButton: 'Cancel',
+ danger: false
+);
+```
+
+### Passing default data
+
+You may use the `fill` method to provide a set of default data to the form:
+
+```php
+$project = Project::firstOrFail();
+
+SpladeForm::make()
+ ->fields([...])
+ ->fill($project);
+```
+
+Note that the Eloquent attributes are passed to the frontend, so be careful with sensitive attributes. By default, it only passes the attributes to the frontend that you use in the fields. You may use a [Transformer](/transformers.md) to help you safely pass data to the frontend. Also, check out the [Model Binding Attributes](/form-model-binding-attributes.md) section.
+
+## Form Class
+
+Instead of using an *inline* `SpladeForm` in the controller, you may also use a dedicated class. There's an Artisan command to generate a Form class:
+
+```bash
+php artisan make:form CreateUserForm
+```
+
+You'll find the new Form class in the `app/Forms` folder. You may use this class in the controller instead of the *inline* instance:
+
+```php
+use App\Forms\CreateUserForm;
+
+return view('users.create', [
+ 'form' => $form, // [tl! remove]
+ 'form' => CreateUserForm::class, // [tl! add]
+]);
+```
+
+The generated `CreateUserForm` will have a `configure()` and `fields()` method:
+
+```php
+action(route('users.store'));
+ }
+
+ public function fields(): array
+ {
+ return [
+ Input::make('name')->label('User Name'),
+ Password::make('password')->label('Password'),
+ Submit::make()->label('Create'),
+ ];
+ }
+}
+```
+
+### Validation
+
+You may pass validation rules to the fields using the `rules()` method:
+
+```php
+Input::make('name')->rules('required', 'max:255');
+
+// or:
+
+Input::make('name')->rules(['required', 'max:255']);
+
+// or:
+
+Input::make('name')->rules('required|max:255');
+```
+
+There's a `required()` helper method that adds the *required* rule to the field:
+
+```php
+Input::make('name')->required();
+```
+
+You may gather all rules using the static `rules()` method on the form class, which you can use in the controller to validate an incoming request:
+
+```php
+public function store(Request $request)
+{
+ $data = $request->validate(CreateUserForm::rules());
+}
+```
+
+Alternatively, you may call the `validate()` method on the form instance. This works great with a type-hinted form variable:
+
+```php
+public function store(Request $request, CreateUserForm $form)
+{
+ $data = $form->validate($request);
+}
+```
+
+### Asterisk on required fields
+
+Splade can automatically add an asterisk to required fields. You may enable this feature by setting the `blade.asterisk_on_required_form_elements` key in the `splade.php` [configuration file](/customization.md) to `true`.
+
+### Using Form Requests
+
+Instead of validating the request in the controller, you may also use Laravel's [Form Requests](https://laravel.com/docs/10.x/validation#form-request-validation) feature. To may use the form's validation rules in the Form Request, and to make it even more seamless, there's an Artisan command to generate a Form Request class:
+
+```bash
+php artisan make:form-request CreateUserFormRequest --form=CreateUserForm
+```
+
+Alternatively, you can generate both at once using `-r` or `--request` option on the `make:form` command:
+
+```bash
+php artisan make:form CreateUserForm --request
+```
+
+This will generate a Form Request with the `rules()` method already implemented:
+
+```php
+ CreateUserForm::make()->fields([
+ Text::make('additional_field')->label('This is an additional field'),
+ ]),
+]);
+```
+
+## Extending Form Fields
+
+All Form Fields are *macroable*, allowing you to easily add new methods:
+
+```php
+use ProtoneMedia\Splade\FormBuilder\Input;
+
+Input::macro('autocomplete', function ($value) {
+ return $this->attributes(['autocomplete' => $value]);
+});
+
+Input::make('password')->autocomplete('current-password');
+```
+
+## Frontend behavior
+
+In addition to configuring the form, fields, rules, and filled data, you may also configure its frontend behavior:
+
+### Prevent navigation on submit
+
+You may instruct Splade to stay on the same page after a successful request:
+
+```php
+SpladeForm::make()->stay();
+```
+
+In additional, you may specify to reset or restore the form on success:
+
+```php
+SpladeForm::make()->stay(actionOnSuccess: 'reset');
+SpladeForm::make()->stay(actionOnSuccess: 'restore');
+```
+
+### Preserve Scroll
+
+When you stay on the same page after a succesful request, you may prevent the page from scrolling to the top:
+
+```php
+SpladeForm::make()->stay()->preserveScroll();
+```
+
+### Submit on change
+
+You may watch form values, and automatically submit the form on changes.
+
+```php
+SpladeForm::make()->submitOnChange(watchFields: 'theme');
+
+// or:
+
+SpladeForm::make()->submitOnChange(watchFields: ['theme', 'scale']);
+```
+
+You may omit the fields to watch all fields, as well as customize the debounce value and performing the request in the background:
+
+```php
+SpladeForm::make()->submitOnChange();
+SpladeForm::make()->submitOnChange(background: true);
+SpladeForm::make()->submitOnChange(debounce: 1000);
+```
+
+> **Warning**
+> The `submitOnChange()` method will not show a confirmation or password dialog.
\ No newline at end of file
diff --git a/form-checkbox.md b/form-checkbox.md
index 0a7ac85..980f491 100644
--- a/form-checkbox.md
+++ b/form-checkbox.md
@@ -1,9 +1,15 @@
# X-Splade-Checkbox Component
-The **Checkbox Component** has a default value of `1`, but you may customize it with the `value` attribute:
+The **Checkbox Component** has a default value of `true`, but you may customize it with the `value` attribute:
```blade
-
+
+```
+
+The default `true` value works great if you use the `boolean` type [Attribute Casting](https://laravel.com/docs/10.x/eloquent-mutators#attribute-casting) in Eloquent Models. Accordingly, the *unchecked* value is `false`, but you may customize it with the `false-value` attribute:
+
+```blade
+
```
If you have a fieldset of multiple checkboxes, you can group them with the `x-splade-group` component. A group is a great way to handle the validation of arrays. If you disable the errors on the individual checkboxes, it will show the validation errors once. The [group component](/form-group.md) has a `show-errors` attribute that defaults to `true`.
diff --git a/form-custom.md b/form-custom.md
new file mode 100644
index 0000000..9c3f329
--- /dev/null
+++ b/form-custom.md
@@ -0,0 +1,3 @@
+# Custom Form components
+
+You can build custom form components with either Blade or Vue. Check out the [extensive guide](/custom-form-components.md)!
\ No newline at end of file
diff --git a/form-file.md b/form-file.md
index 2cdd4c9..5d3b15c 100644
--- a/form-file.md
+++ b/form-file.md
@@ -1,3 +1,9 @@
+---
+description: The Splade File component can display the selected file and integrates with FilePond and Spatie's Media Library. It supports handling existing files, reordering files, async uploads, and image validation.
+
+keywords: laravel file upload, laravel filepond, filepond spatie media library, filepond file manager, laravel file manager, laravel image library, laravel async upload, laravel image preview, laravel multiple file upload
+---
+
# X-Splade-File Component
The **File Component** is a dedicated component you can use to select files. This component is based mainly on the [Input component](/form-input.md) but provides additional logic to display the filename of the selected file. You don't have to manually use the input event to bind the selected files to the form. Splade does this for you.
@@ -12,6 +18,353 @@ The component supports selecting multiple files as well by adding the `multiple`
```blade
-
+
+
+```
+
+## Image preview
+
+You might want to show a preview of the selected image. You may use the `form.$fileAsUrl` method to generate a *base64-encoded* representation of the image, and use it as the source of an `img` element. In addition, with the `show-filename` attribute, you can disable displaying the filename.
+
+```blade
+
+
+
+
+
+ ```
+
+## FilePond
+
+The [FilePond](https://pqina.nl/filepond/) integration comes with a default stylesheet which you should import into the main JavaScript file. If you've used the automatic installer, it has already done this for you.
+
+```js
+import "@protonemedia/laravel-splade/dist/style.css";
+import { renderSpladeApp, SpladePlugin } from "@protonemedia/laravel-splade";
+```
+
+Now you may add the `filepond` attribute to the component:
+
+```blade
+
+```
+
+This works for uploading multiple files as well:
+
+```blade
+
+```
+
+You can instantiate FilePond with a [custom set of options](https://pqina.nl/filepond/docs/api/instance/properties/) by passing a *JavaScript* object to the `filepond` attribute. To pass a PHP array, you may use the `:filepond` attribute (note the colon).
+
+```blade
+
+
+
+```
+
+### Image Preview
+
+FilePond can render a downscaled preview of the selected image by adding the `preview` attribute to the component:
+
+```blade
+
+```
+
+### Validate type and size
+
+FilePond supports validating the selected file based on the type and size. To validate the type, you may use the `accept` attribute:
+
+```blade
+
+```
+
+You may use the `min-size` or `max-size` attribute to validate the file size. You may use both attributes at once as well:
+
+```blade
+
+```
+
+Please be aware that both features are *client-side* validation. For security reasons, make sure you use [server-side validation](https://laravel.com/docs/10.x/validation#validating-files) as well.
+
+### Validate images
+
+FilePond supports validating the dimensions of a selected image. For example, you may use the `min-width` and `min-height` attributes to validate the minimum width and height. Similarly, to validate the maximum width and height, you may use the `max-width` and `max-height` attributes:
+
+```blade
+
+
+
+```
+
+If you want to validate against an exact size, you may use the `width` and `height` attributes:
+
+```blade
+
+```
+
+Alternatively, you may specify a minimum or maximum resolution:
+
+```blade
+
+
+
+```
+
+### Adding remote files
+
+FilePond can upload a file using a Remote URL. The `form` object has an `$addFile` method that allows you to pass a URL to a FilePond instance. You must pass the *field* as the first argument and the *URL* as the second argument. In the example below, we'll use a regular input element to set the Remote URL:
+
+```blade
+
+
+
+
+
+
+ Add from Remote URL
+
+
+```
+
+If you need to add multiple files at once, you may use the `$addFiles` method, which accepts an array as a second argument.
+
+### Asynchronous uploads
+
+FilePond supports uploading the file to the server before the form is submitted. First, you must register a supporting route using the `spladeUploads()` method on the `Route` facade. As of version 0.7.6, 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::spladeUploads();
+```
+
+Next, in the template, add the `server` attribute to the component. From now on, when a user drops a file into the FilePond instance, it will immediately start uploading to the server.
+
+```blade
+
+```
+
+Splade will store the file in a temporary directory and report the path to the file back to the File component. So when the user submits the form, it will send this path instead of uploading the file.
+
+There are three ways of handling the temporary upload. You may choose the option that best fits your needs. First, you may use the `HandleSpladeFileUploads` class, for example, in your controller:
+
+```php
+use Illuminate\Http\Request;
+use ProtoneMedia\Splade\FileUploads\HandleSpladeFileUploads; // [tl! add]
+
+public function store(Request $request)
+{
+ HandleSpladeFileUploads::forRequest($request); // [tl! add]
+
+ $request->validate([
+ 'photo' => ['required', 'file', 'image'],
+ ]);
+
+ $path = $request->file('photo')->store('images');
+}
+```
+
+The `HandleSpladeFileUploads` class will loop through the request data and transform paths to temporary uploads back into `UploadedFile` instances. Make sure you call the `forRequest()` method *before* validating the request. Instead of looping through all request data, you may also pass the key (or an array of keys):
+
+```php
+HandleSpladeFileUploads::forRequest($request, 'photo');
+```
+
+The second option is to use a Route Middleware. You may use the same `HandleSpladeFileUploads` class as the example above. Using the *fully qualified class name* will loop through all request data, but you may also specify one or more keys using the static `for` method.
+
+```php
+Route::post('podcast', StorePodcastController::class)
+ ->middleware(HandleSpladeFileUploads::class); // [tl! add]
+
+Route::post('podcast', StorePodcastController::class)
+ ->middleware(HandleSpladeFileUploads::for('photo')); // [tl! add]
+```
+
+The last option is to use a [Form Request](https://laravel.com/docs/10.x/validation#form-request-validation). Then, you only have to implement the `HasSpladeFileUploads` interface and use the `file` validation rule. Splade will automatically extract the keys from the rules.
+
+```php
+use Illuminate\Foundation\Http\FormRequest;
+use ProtoneMedia\Splade\FileUploads\HasSpladeFileUploads; // [tl! add]
+
+class StorePodcastRequest extends FormRequest implements HasSpladeFileUploads // [tl! add]
+{
+ public function rules()
+ {
+ return [
+ 'photo' => 'required|file|image',
+ ];
+ }
+}
+```
+
+### Cleanup temporary uploads
+
+It may happen that temporarily uploaded files are not being used and will fill the temporary directory. Splade comes with a built-in Artisan command to delete all unused files that are older than one hour:
+
+```bash
+php artisan splade:cleanup-uploads
+```
+
+You may change the lifetime of temporary files with the `file_uploads.temporary_file_lifetime` key in the `splade.php` [configuration file](/customization.md).
+
+### Custom temporary directory
+
+By default, Splade uses the `/storage/splade-temporary-file-uploads` directory for temporary uploads. If you want to use a custom [Filesystem disk](https://laravel.com/docs/10.x/filesystem#configuration), you may update the `file_uploads.disk` key in the `splade.php` [configuration file](/customization.md). For now, it only supports local disks.
+
+### Customize FilePond styling
+
+FilePond uses an *SCSS* stylesheet to style the library. Our stylesheet extends the vendor stylesheet (of FilePond) and adds some Tailwind-specific tweaks. Make sure your bundler handles SCSS stylesheets correctly, for example, by installing `sass`. The `splade:publish-form-stylesheets` Artisan command copies the stylesheet to the `resources` directory of your app.
+
+```bash
+npm install sass -D
+
+php artisan splade:publish-form-stylesheets
+```
+
+Then import the stylesheet in your main JavaScript file (instead of the default `@protonemedia/laravel-splade/dist/style.css` stylesheet):
+
+```js
+import "../css/filepond.scss"
+```
+
+## Working with existing files
+
+FilePond allows you to show existing files in the UI. Splade comes with a set of tools to help you present and preserve existing files. Imagine you use the file input to replace a user's avatar while still showing the current one. You may use the `ExistingFile` class to load the current avatar:
+
+```php
+use ProtoneMedia\Splade\FileUploads\ExistingFile;
+
+$avatar = ExistingFile::fromDisk('public')->get('avatars/user.jpeg');
+```
+
+Then in the Blade template, you may use the `ExistingFile` instance as default form data:
+
+```blade
+
+
+
+
+```
+
+If you submit the form *without* changing the avatar, the `avatar` field will be empty, but Splade will submit an `avatar_existing` field to inform you the current avatar hasn't changed. When you access that key from the `Request` instance, it will give you an instance of `ExistingFile` again:
+
+```php
+public function update(Request $request)
+{
+ HandleSpladeFileUploads::forRequest($request);
+
+ // This is an instance of ExistingFile:
+ $existingAvatar = $request->avatar_existing;
+}
+```
+
+### Multiple existing files
+
+You may also use multiple existing files with a file input that allows uploading multiple files. For example, instead of passing one instance of `ExistingFile`, you may now use an array:
+
+```php
+$photos = [
+ ExistingFile::fromDisk('public')->get('photos/1.jpeg'),
+ ExistingFile::fromDisk('public')->get('photos/2.jpeg'),
+];
+```
+
+Luckily, the `get` method also accepts an array:
+
+```php
+$photos = ExistingFile::fromDisk('public')->get([
+ 'photos/1.jpeg',
+ 'photos/2.jpeg',
+]);
+```
+
+Just like using a single existing file as default form data, you may do the same for multiple files:
+
+```blade
+
+
+
-```
\ No newline at end of file
+```
+
+The user may submit the form with existing and new files when dealing with multiple files. In addition, FilePond allows the user to reorder the files as well. So how would you handle such forms in the controller? Like the avatar example, Splade will submit a `photos` array with new files and a `photos_existing` array with the existing files. Additionally, the request data will have a `photos_order` key representing the order, as a user can mix existing and new files.
+
+You can combine existing and new files with their order manually. However, Splade comes with a `orderedSpladeFileUploads()` method that you may call on a `Request` instance. This method returns a `Collection` with `SpladeFile` instances. The files are already in the correct order, and have helper methods like `exists()` and `doesntExist()` to determine the file's nature.
+
+```php
+use ProtoneMedia\Splade\FileUploads\SpladeFile;
+
+public function update(Request $request)
+{
+ $request->orderedSpladeFileUploads('photos')->each(function (SpladeFile $file) {
+ if ($file->exists()) {
+ // This is an instance of ExistingFile:
+ $file->existing;
+ }
+
+ if ($file->doesntExist()) {
+ // This is an instance of UploadedFile:
+ $file->upload;
+ }
+ })
+}
+```
+
+### Spatie Media Library
+
+The FilePond integration has built-in support for [Spatie's Media Library package](https://spatie.be/docs/laravel-medialibrary/v10/introduction). The `ExampleFile` class has a helper method to load media collections. Instead of loading a collection, it also works with the `getFirstMedia()` method.
+
+```php
+ExistingFile::fromMediaLibrary($model->getMedia());
+```
+
+Additionally, you may specify the [conversion name](https://spatie.be/docs/laravel-medialibrary/v10/converting-images/defining-conversions) that FilePond should use for a preview image. There's also support for customizing the expiration and options of the Temporary URL (S3 disk only).
+
+```php
+ExistingFile::fromMediaLibrary(
+ media: $model->getMedia(),
+ previewConversionName: 'thumb',
+ previewUrlExpiration: now()->addMinutes(15),
+ previewUrlOptions: ['ResponseContentType' => 'application/octet-stream']
+);
+```
+
+Instead of using the `orderedSpladeFileUploads()` method and syncing the media collection, you may use the `syncMediaLibrary` method. This method will add and delete files and set the order. You must pass the `Request` instance, the Eloquent model that interacts with media (the subject), and the request key containing the files.
+
+```php
+public function update(Request $request)
+{
+ $user = $request->user();
+
+ HandleSpladeFileUploads::syncMediaLibrary($request, $user, 'photos');
+}
+```
+
+Optionally, there's a fourth and fifth argument to customize the name of the media collection and the name of the disk:
+
+```php
+HandleSpladeFileUploads::syncMediaLibrary(
+ request: $request,
+ subject: $user,
+ key: 'photos',
+ collectionName: 'photos',
+ diskName: 's3'
+);
+```
+
+### Additional helper methods
+
+To help you identify existing files, you may set an array with metadata:
+
+```php
+$existingFile->metadata(['id' => $template->id]);
+```
+
+To retrieve the metadata, you may call the `getMetadata()` method to get the array, or pass a key to retrieve a value:
+
+```php
+$allMetadata = $existingFile->getMetadata();
+
+$id = $existingFile->getMetadata('id');
+```
+
+Splade will automatically serialize Eloquent models and collections, and the metadata will be encrypted before it's passed to the Vue frontend.
\ No newline at end of file
diff --git a/form-input.md b/form-input.md
index 3d59f9a..f4e2445 100644
--- a/form-input.md
+++ b/form-input.md
@@ -95,4 +95,6 @@ Then import the stylesheet in your main JavaScript file (instead of the default
```js
import "../css/flatpickr.styl"
-```
\ No newline at end of file
+```
+
+In a future Splade release, we'll work on migrating the *Stylus* stylesheet to a more Tailwind-friendly preprocessor (or plain CSS).
\ No newline at end of file
diff --git a/form-model-binding-attributes.md b/form-model-binding-attributes.md
index 61ee5db..be22bed 100644
--- a/form-model-binding-attributes.md
+++ b/form-model-binding-attributes.md
@@ -35,4 +35,6 @@ use ProtoneMedia\Splade\Components\Form;
Form::guardWhen(function ($resource) {
return $resource instanceof Model;
});
-```
\ No newline at end of file
+```
+
+Also, check out the [Transformers](/transformers.md) section.
\ No newline at end of file
diff --git a/form-overview.md b/form-overview.md
index 11bdd82..10805a9 100644
--- a/form-overview.md
+++ b/form-overview.md
@@ -1,6 +1,6 @@
# Form Components
-Splade comes with a set of **Form Components** to rapidly build forms. It supports model binding and validation, includes default styling, and is still fully customizable! It integrates with [Autosize](https://www.jacklmoore.com/autosize/) to automatically adjust textarea height, [Choices.js](https://github.com/Choices-js/Choices) to make selects searchable and taggable, and [Flatpickr](https://flatpickr.js.org) to provide a powerful datetime picker.
+Splade comes with a set of **Form Components** to rapidly build forms. It supports model binding and validation, includes default styling, and is still fully customizable! It integrates with [Autosize](https://www.jacklmoore.com/autosize/) to automatically adjust textarea height, [Choices.js](https://github.com/Choices-js/Choices) to make selects searchable and taggable, [Flatpickr](https://flatpickr.js.org) to provide a powerful datetime picker, [FilePond](https://pqina.nl/filepond/) for smooth file uploads, and [Jodit](https://xdsoft.net/jodit/) to provide a WYSIWYG editor.
Available components:
@@ -12,6 +12,7 @@ Available components:
* [Select](/form-select.md)
* [Submit](/form-submit.md)
* [Textarea](/form-textarea.md)
+* [Wysiwyg](/form-wysiwyg.md)
@@ -25,11 +26,11 @@ All elements allow passing in a label, which will be rendered above the input el
-
+
```
-If you want to eliminate the `splade` prefix, you may update the `blade.component_prefix` key in the `splade.php` configuration file:
+If you want to eliminate the `splade` prefix, you may update the `blade.component_prefix` key in the `splade.php` [configuration file](/customization.md):
```php
return [
@@ -49,7 +50,7 @@ This will result in more readable ``, ``, and `` comp
## Model Binding
-To bind a resource to a form, for example, an [Eloquent Model](https://laravel.com/docs/9.x/eloquent), you may use the `default` attribute. Instead of using the `v-model` attribute on the input element, you may now use the `name` attribute. Splade will take care of the two-way binding with the User model.
+To bind a resource to a form, for example, an [Eloquent Model](https://laravel.com/docs/10.x/eloquent), you may use the `default` attribute. Instead of using the `v-model` attribute on the input element, you may now use the `name` attribute. Splade will take care of the two-way binding with the User model.
```blade
@@ -81,4 +82,10 @@ By default, Splade scrolls to the first element with a validation error. You may
...
-```
\ No newline at end of file
+```
+
+Splade escapes Validation errors by default, so if you use raw HTML in one of your messages, Vue won't insert those as plain HTML. If you want to disable this behavior, set the `blade.escape_validation_messages` key in the `splade.php` [configuration file](/customization.md) to `false`. Only disable this on trusted content and never on user-provided content, as it may lead to XSS attacks.
+
+## Form Builder
+
+In addition to building forms in Blade templates, you may also build forms in PHP with the [Form Builder](/form-builder-overview.md).
diff --git a/form-select.md b/form-select.md
index e9646a3..a1bcf13 100644
--- a/form-select.md
+++ b/form-select.md
@@ -1,6 +1,6 @@
# X-Splade-Select Component
-The **Select Component** can render the options based on an array, with support for groups.
+The **Select Component** can render the options based on a *key-value* array or `Collection`, with support for groups.
```php
$countries = [
@@ -13,6 +13,12 @@ $countries = [
```
+You may also pass a set of objects, like an Eloquent Collection, and specify the *value* and *label* keys for the option elements:
+
+```blade
+
+```
+
You can provide a slot to the select element as well:
```blade
@@ -75,7 +81,7 @@ Now you may add the `choices` attribute to the component:
It works for selecting multiple values as well:
```blade
-
+
```
You can instantiate Choices.js with a [custom set of options](https://github.com/Choices-js/Choices#setup) by passing a *JavaScript* object to the `choices` attribute. To pass a PHP array, you may use the `:choices` attribute (note the colon).
@@ -104,9 +110,80 @@ Select::defaultChoices([
]);
```
+### Remote Options
+
+The component has support for loading the options using an asynchronous *ajax* request. For this, you may use the `remote-url` attribute:
+
+```blade
+
+```
+
+Just like the regular pre-defined options, it supports objects as well:
+
+```blade
+
+```
+
+You might want to load the remote options from a nested dataset. In the example below, you want to extract the options from the `data.users` path.
+
+```json
+{
+ "data": {
+ "users": [
+ {
+ "id": 1,
+ "name": "John"
+ },
+ {
+ "id": 2,
+ "name": "Jane"
+ }
+ ]
+ }
+}
+```
+
+You may use the `remote-root` attribute to set a base path for the options:
+
+```blade
+
+```
+
+### Dynamic Remote URL
+
+The `remote-url` attribute supports *Template literals*, making it perfect for building dependent selects:
+
+```blade
+
+
+```
+
+When the user selects a country, it will reload the regions based on the chosen country.
+
+When you're dealing with dynamic options, you probably don't know the option values beforehand. Therefore, you may instruct the component to automatically choose the first option of the freshly loaded options using the `select-first-remote-option` attribute:
+
+```blade
+
+```
+
+Additionally, you may clear the selected option whenever the Remote URL changes using the `reset-on-new-remote-url` attribute:
+
+```blade
+
+```
+
+Both options can be configured to be applied by default. You may use the static methods on the `Select` class, for example, in the `AppServiceProvider` class:
+
+```php
+Select::defaultResetOnNewRemoteUrl();
+Select::defaultSelectFirstRemoteOption();
+```
+
+All remote options features work with the Choices.js integration as well. It doesn't support groups yet, but that's coming in a future version of Splade.
+
### Customize Choices.js styling
-Choices.js uses a *SCSS* stylesheet to style the library. Our stylesheet extends the vendor stylesheet (of Choices.js) and adds some Tailwind-specific tweaks. Make sure your bundler handles SCSS stylesheets correctly, for example, by installing `sass`. The `splade:publish-form-stylesheets` Artisan command copies the stylesheet to the `resources` directory of your app.
+Choices.js uses an *SCSS* stylesheet to style the library. Our stylesheet extends the vendor stylesheet (of Choices.js) and adds some Tailwind-specific tweaks. Make sure your bundler handles SCSS stylesheets correctly, for example, by installing `sass`. The `splade:publish-form-stylesheets` Artisan command copies the stylesheet to the `resources` directory of your app.
```bash
npm install sass -D
@@ -118,4 +195,22 @@ Then import the stylesheet in your main JavaScript file (instead of the default
```js
import "../css/choices.scss"
-```
\ No newline at end of file
+```
+
+### Laravel Dusk macro
+
+Splade has two macros that help you test Choices.js instances with [Laravel Dusk](https://laravel.com/docs/10.x/dusk). Instead of calling `select` with the *field* and *value* arguments, you may use the `choicesSelect` method:
+
+```php
+$browser->select('size', 'Large'); // [tl! remove]
+
+$browser->choicesSelect('size', 'Large'); // [tl! add]
+```
+
+If you want to remove an item from a Choices.js instance with multiple options, you may use the `choicesRemoveItem` method:
+
+```php
+$browser->choicesRemoveItem('countries[]', 'NL');
+```
+
+You may change the name of the macros with the `dusk.choices_select_macro` and `dusk.choices_remove_item_macro` keys in the `splade.php` [configuration file](/customization.md).
\ No newline at end of file
diff --git a/form-submit.md b/form-submit.md
index 08d257c..418be3c 100644
--- a/form-submit.md
+++ b/form-submit.md
@@ -3,7 +3,7 @@
The **Submit Component** has a default label and spinner visible when processing the form. You may change the default text using the `label` attribute, and you may disable the spinner as well.
```blade
-
+
```
You may also use a slot:
@@ -13,4 +13,12 @@ You may also use a slot:
...
Send now
+```
+
+In addition to providing a custom template with a slot, you may change the styling of the default submit button with the `danger` or `secondary` attributes:
+
+```blade
+
+
+
```
\ No newline at end of file
diff --git a/form-wysiwyg.md b/form-wysiwyg.md
new file mode 100644
index 0000000..4559782
--- /dev/null
+++ b/form-wysiwyg.md
@@ -0,0 +1,41 @@
+# X-Splade-Wysiwyg Component
+
+The **Wysiwyg Component** provides an integration for [Jodit Editor 3](https://xdsoft.net/jodit/). Since Jodit's stylesheet is quite large, it is not included in the Splade bundle by default. You may include it in your application by adding `jodit.css` to your main `app.js` file:
+
+```js
+import "./bootstrap";
+import "../css/app.css";
+import "@protonemedia/laravel-splade/dist/style.css";
+import "@protonemedia/laravel-splade/dist/jodit.css"; // [tl! add]
+
+import { createApp } from "vue/dist/vue.esm-bundler.js";
+import { renderSpladeApp, SpladePlugin } from "@protonemedia/laravel-splade";
+```
+
+Then you may use the component:
+
+```blade
+
+```
+
+## Configuration
+
+You can instantiate Jodit with a [custom set of options](https://xdsoft.net/jodit/docs/classes/config.Config.html) by passing a *JavaScript* object to the `jodit` attribute. To pass a PHP array, you may use the `:jodit` attribute (note the colon).
+
+```blade
+
+
+
+```
+
+### Default settings
+
+Suppose you want to specify a default set of options for all Jodit instances. In that case, you may use the static `defaultJoditOptions` method on the `Wysiwyg` class, for example, in the `AppServiceProvider` class:
+
+```php
+use ProtoneMedia\Splade\Components\Form\Wysiwyg;
+
+Wysiwyg::defaultJoditOptions([
+ 'showXPathInStatusbar' => true,
+]);
+```
diff --git a/how-splade-works.md b/how-splade-works.md
index 1cff9eb..0d2dbd3 100644
--- a/how-splade-works.md
+++ b/how-splade-works.md
@@ -12,7 +12,7 @@ Splade is super easy to use but rather complicated under the hood. Let's break d
First, the Blade compiler will render the `x-data` component. This Blade component is essentially a wrapper around a corresponding Vue component. It understands that the given data (the default array) is *jsonable*, so it passes the data as a JSON-encoded string to the `SpladeData` Vue component as a property.
-```vue
+```blade
diff --git a/index.json b/index.json
new file mode 100644
index 0000000..f16339d
--- /dev/null
+++ b/index.json
@@ -0,0 +1,79 @@
+{
+ "Introduction": {
+ "Introducing Splade": "introducing-splade",
+ "Credits": "credits",
+ "Automatic installation": "automatic-installation",
+ "Manual installation": "manual-installation",
+ "Breeze starter kit": "breeze",
+ "Jetstream starter kit": "jetstream",
+ "Upgrading": "upgrading",
+ "Roadmap": "roadmap"
+ },
+ "Basics": {
+ "Navigation and routing": "navigation-routing",
+ "Progress bar": "progress-bar",
+ "Toasts": "toasts",
+ "Shared data": "shared-data",
+ "Title and meta tags": "title-meta",
+ "Transformers": "transformers",
+ "Lazy loading": "lazy-loading",
+ "Server-side rendering (SSR)": "ssr"
+ },
+ "Components": {
+ "Content": "x-content",
+ "Data": "x-data",
+ "Defer": "x-defer",
+ "Errors": "x-errors",
+ "Event": "x-event",
+ "Flash": "x-flash",
+ "Form": "x-form",
+ "Link": "x-link",
+ "Modal": "x-modal",
+ "Rehydrate": "x-rehydrate",
+ "Script": "x-script",
+ "State": "x-state",
+ "Teleport": "x-teleport",
+ "Toggle": "x-toggle",
+ "Transition": "x-transition",
+ "Custom components": "form-custom"
+ },
+ "Form components": {
+ "Overview": "form-overview",
+ "Input": "form-input",
+ "Textarea": "form-textarea",
+ "Select": "form-select",
+ "Checkbox": "form-checkbox",
+ "Radio": "form-radio",
+ "File": "form-file",
+ "Group": "form-group",
+ "Submit": "form-submit",
+ "Wysiwyg (Jodit)": "form-wysiwyg",
+ "Model Binding": "form-model-binding-attributes"
+ },
+ "Form Builder": {
+ "Overview": "form-builder-overview",
+ "Available fields": "form-builder-fields"
+ },
+ "Table component": {
+ "Overview": "table-overview",
+ "Built-in Query Builder": "table-query-builder",
+ "Bulk Actions": "table-bulk-actions",
+ "Exports": "table-exports",
+ "Spatie Query Builder": "table-spatie-query-builder"
+ },
+ "Vue components and libraries": {
+ "Custom components": "custom-vue-components",
+ "Vue libraries": "using-vue-libraries",
+ "Form components": "custom-form-components"
+ },
+ "Advanced": {
+ "State management": "state-management",
+ "Persistent layout": "persistent-layout",
+ "Event bus": "event-bus",
+ "Bridge Components (Beta)": "bridge-components",
+ "Customization": "customization",
+ "Translations": "translations",
+ "Exception handling": "exception-handling",
+ "How Splade works": "how-splade-works"
+ }
+}
\ No newline at end of file
diff --git a/introducing-splade.md b/introducing-splade.md
index 404f5a2..f87f2b9 100644
--- a/introducing-splade.md
+++ b/introducing-splade.md
@@ -1,6 +1,6 @@
# Introducing Splade
-Splade provides a **super easy** way to build *Single Page Applications* (SPA) using standard [Laravel Blade](https://laravel.com/docs/9.x/blade) templates, enhanced with [renderless Vue 3 components](https://adamwathan.me/renderless-components-in-vuejs/). In essence, you can write your app using the simplicity of Blade, and besides that magic *SPA-feeling*, you can sparkle it to make it interactive. All without ever leaving Blade.
+Splade provides a **super easy** way to build *Single Page Applications* (SPA) using standard [Laravel Blade](https://laravel.com/docs/10.x/blade) templates, enhanced with [renderless Vue 3 components](https://adamwathan.me/renderless-components-in-vuejs/). In essence, you can write your app using the simplicity of Blade, and besides that magic *SPA-feeling*, you can sparkle it to make it interactive. The best part? You can do all of this without ever leaving Blade! Splade makes it easy to create modern, dynamic web applications that are a joy to use.
**Wow! Sounds interesting? Let's take a closer look.**
@@ -13,11 +13,11 @@ To leverage the SPA capabilities of Splade, you may use the ` ` component i
Contact
```
-If you're using an existing app, or maybe you don't like the `Link` component, you may configure Splade to transform all `` elements automatically.
+If you're using an existing app, or maybe you don't like the `Link` component, you may configure Splade to transform all ` ` elements automatically as described in the [Link Component section](/x-link.md).
## The simplest component: a toggle
-In the example below, we show an excerpt of the blog post. When the user clicks on the *Expand* button, it hides the excerpt and shows the full content. All Splade components are default prefixed with `splade`, but you may configure it without a prefix, resulting in a more readable `` component.
+In the example below, we show an excerpt of the blog post. When the user clicks on the *Expand* button, it hides the excerpt and shows the full content. All Splade components are default prefixed with `splade`, but you may [configure it](/customization.md) without a prefix, resulting in a more readable `` component.
```blade
@extends('layout')
@@ -68,7 +68,7 @@ The dedicated form component allows you to send forms asynchronously. It catches
First, let's explain the `x-form` component: it's a wrapper around a Vue component. Yes, each Splade component consists of a renderless Vue component (where all interactive magic happens) wrapped in a Blade component.
-In the following example, the form component is given some default data, that of the authenticated user. Note that the user data is passed to the frontend, so please be sure sensitive attributes [hidden](https://laravel.com/docs/9.x/eloquent-serialization#hiding-attributes-from-json).
+In the following example, the form component is given some default data, that of the authenticated user. Note that the user data is passed to the frontend, so please be sure sensitive attributes are [hidden](https://laravel.com/docs/10.x/eloquent-serialization#hiding-attributes-from-json), or use the built-in [Transformer feature](/transformers.md).
```blade
@@ -102,4 +102,4 @@ Here's a summary of all the cool things Splade can do:
* `` component to add animations to your templates.
* `Toast` component to display toasts on your page. It supports nine positions, four styles, backdrop background, and auto-dismiss.
* `Table` component to automatically generate tables with support for auto-fill, searching, filtering, sorting, toggling columns, and pagination.
-* `Form` components to rapidly build forms. Support for model binding, validation, Eloquent Relationships, textarea autosize, Choices.js searchable/taggable selects, and Flatpickr for date/time/range picking.
\ No newline at end of file
+* `Form` components to rapidly build forms. Support for model binding, validation, Eloquent Relationships, textarea autosize, Choices.js searchable/taggable selects, Flatpickr for date/time/range picking, and FilePond file uploads.
\ No newline at end of file
diff --git a/jetstream.md b/jetstream.md
new file mode 100644
index 0000000..85be927
--- /dev/null
+++ b/jetstream.md
@@ -0,0 +1,21 @@
+# Jetstream Starter Kit
+
+You can use the [Laravel Jetstream](https://laravel.com/docs/10.x/starter-kits#laravel-jetstream) starter kit to give you a head start. Jetstream provides a beautifully designed application scaffolding for Laravel and includes login, registration, email verification, two-factor authentication, session management, API support via Laravel Sanctum, and optional team management. 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). Note that Jetstream for Splade requires Laravel 10.
+
+```bash
+laravel new example-app
+
+cd example-app
+
+composer require protonemedia/laravel-splade-jetstream
+
+php artisan jetstream:install --teams --api --verification
+```
+
+The `jetstream: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/lazy-loading.md b/lazy-loading.md
index 0e0acc8..f2a9473 100644
--- a/lazy-loading.md
+++ b/lazy-loading.md
@@ -12,6 +12,8 @@ The **Lazy Component** allows you to load sections of your template lazily. Wrap
```
+
+
## Lazy View Data
While excluding a section from your template is nice, you probably also want to exclude data from the initial page load. Most commonly, this is the data you need in your lazily loaded content. You may use the `onInit` and `onLazy` methods on the `Splade` facade to wrap the data in a closure.
@@ -73,4 +75,24 @@ If you don't want to include the content directly into the slot, you may define
...
+```
+
+## Conditionals
+
+Don't wrap the Lazy Component into an *if-statement*, but always put the statement *within* the component:
+
+```blade
+
+@if($user->canViewNewsItems())
+
+ ...
+
+@endif
+
+
+
+ @if($user->canViewNewsItems())
+ ...
+ @endif
+
```
\ No newline at end of file
diff --git a/manual-installation.md b/manual-installation.md
index 2386ed5..77a7348 100644
--- a/manual-installation.md
+++ b/manual-installation.md
@@ -48,7 +48,7 @@ class Handler extends ExceptionHandler
## Client-side
-On the frontend, you must ensure [Tailwind CSS 3.0](https://tailwindcss.com) and [Vue 3.0](https://vuejs.org) are correctly configured. Then, install the Splade frontend package:
+On the frontend, you must ensure [Tailwind CSS 3.0](https://tailwindcss.com/docs/guides/laravel) and [Vue 3.0](https://vuejs.org/guide/quick-start.html) are correctly configured. Then, install the Splade frontend package:
```bash
npm install @protonemedia/laravel-splade
@@ -69,12 +69,12 @@ module.exports = {
};
```
-In the `createApp` section of your main JavaScript file, you need to use the Splade plugin, as well as the custom render method:
+In the `createApp` section of your main JavaScript file, you need to use the Splade plugin, as well as the custom render method. Note how it imports the `createApp` method from the *bundler* Vue build, as that build includes the [Vue template compiler](https://vuejs.org/guide/scaling-up/tooling.html#note-on-in-browser-template-compilation).
```js
import "@protonemedia/laravel-splade/dist/style.css";
-import { createApp } from 'vue'
+import { createApp } from "vue/dist/vue.esm-bundler.js";
import { renderSpladeApp, SpladePlugin } from '@protonemedia/laravel-splade'
const el = document.getElementById('app')
@@ -86,7 +86,7 @@ createApp({
.mount(el);
```
-As you can see at the top, there's also a default stylesheet to support the Choices.js and Flatpickr integrations of the [Form Components](/form-overview.md). Though you probably want to import this default stylesheet into your main JavaScript file, it's completely optional.
+As you can see at the top, there's also a default stylesheet to support the Choices.js, FilePond, and Flatpickr integrations of the [Form Components](/form-overview.md). Though you probably want to import this default stylesheet into your main JavaScript file, it's completely optional.
In your Blade root layout, you may use the `@splade` directive inside the `body`, and the `@spladeHead` directive inside the `head`. This will render the title and meta tags, and the default `
` element where the Vue app will be mounted.
@@ -100,24 +100,26 @@ In your Blade root layout, you may use the `@splade` directive inside the `body`
+ @splade
+
```
-Splade assumes the path of this file is `resources/views/root.blade.php`. If you want to change it, you may call the `setRootView` method on the Splade facade, for example, in the `AppServiceProvider` class:
+Here's an example of a sensible root layout:
-```php
-Splade::setRootView('base-layout');
+```blade
+
+
+