From 174c70d2428161d517ccd4c1459847a396ee4c3e Mon Sep 17 00:00:00 2001 From: Gustavo Fenilli Date: Tue, 31 Oct 2023 14:47:39 -0300 Subject: [PATCH] docs: adds documentation at readme.md --- README.md | 483 +++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 480 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index dad5b01..640e7df 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,482 @@ -# FormKit + Inertia +

-[![CI](https://github.com/formkit/inertia/actions/workflows/ci.yml/badge.svg)](https://github.com/formkit/inertia/actions/workflows/ci.yml) +# FormKit Inertia Integration -Readme to come.... + + GitHub Build Status + + + Npm Version + + + Npm Version + + +This project aims to seamlessly integrate Inertia.js with FormKit forms, leveraging a robust event system that harnesses Inertia.js event callbacks and FormKit plugins for a smooth and powerful web development experience, and here is why: + +- Your time should be used on creating forms and backends, not integrations. +- Having an easy to install and use package, makes it so you don't need to care about packaging and publishing. + +### Table of Contents + +1. [Quick Start](#quick-start) +2. [The Composable `useForm()`](#the-composable-useform) + 1. [Method Calls](#method-calls) + 2. [States](#states) + 3. [Event Functions](#event-functions) +3. [Event System](#event-system) +4. [Types](#types) + +## Quick Start + +First, you should already have a Laravel with InertiaJS application, you can check the docs on how [here](https://laravel.com/docs/10.x/starter-kits#breeze-and-inertia). + +You also should already have FormKit added to your vue application, you can check the docs on how [here](https://formkit.com/getting-started/installation). + +Now you have all requisites ready to use this package, you can install it with your preferred package manager. + +```shell +foo@bar:~$ npm install @formkit/inertia +``` + +You should be able to to use the now available composable `useForm()`: + +```ts +import { useForm } from '@formkit/inertia' + +const form = useForm() +const submit = form.post('/login') + + + // The rest of your form goes here + +``` + +And that is it, now you're ready to look more into the available features of this package, like the agnostic [event system]() and more about the [vue composable](). + +

(back to top)

+ +## The Composable `useForm()` + +To make integration between FormKit and Inertia easier we include a form helper composable designed to reduce the boilerplate needed for handling form submissions, it by default creates the event system, add method calls and reactive states, and it will also add default behaviours to FormKit forms like loading, disabling and setting errors that come from your backend. + +> Remember that for everything to work as expected you should add the returned plugin to FormKit: `:plugins="[form.plugin]"`. + +### Method Calls + +To submit the form, `useForm()` returns the methods `get`, `post`, `put`, `patch` and `delete`, any of those will return a suitable function that FormKit `@submit` expects. + +The easiest way to use it is by creating a new `const` with the resulting method of your choice: + +```html + + + +``` + +But you can also manually pass the variables to the returned function: + +```html + + + +``` + +The functions support all [visit options](https://inertiajs.com/manual-visits) from Inertia, such as `preserveState`, `preserveScroll`, and event callbacks. + +> Those will also remove any default events to that specific event, meaning that if you for example add `onStart` you will lose the events from `start` that are for example loading, disabling and processing. + +```html + + + +``` + +To cancel a form submission, use the `cancel()` method. + +```html + + + + + +``` + +

(back to top)

+ +### States + +The `useForm()` returns some helpful reactive states, the Inertia based ones are: `processing`, `progress`, `recentlySuccessful` and `wasSuccessful`, the FormKit based ones are `valid`, `errors`, `dirty` and `node`. + +> All FormKit based states will be null if `form.plugin` wasn't added to the FormKit form input. + +Those events can be helpful for example for disabling the form submit button if you're using your own submit instead of the provided FormKit one: + +> The `node` can be really helpful if you need the underlining [FormKit node](https://formkit.com/essentials/architecture). + +```html + +``` + +

(back to top)

+ +### Event Functions + +If you need to new features, or want to run some code on Inertia event callbacks but want to keep the functionality of this package intact, you can use the provided event functions `on()` and `combine()`, those functions are meant to add functions to the event callbacks without having to deal with option merging. + +The `on()` function accepts any of the events from Inertia event callbacks without the `on` prefix, those being `before`, `start`, `progress`, `success`, `error`, `cancel`, `finish` and the parameters will be always the event callback parameter then FormKit's node: + +> Returning false from the `before` event will cause the visit to be cancelled. + +```html + +``` + +The `combine()` function is just a easier way to add multiple events in a single place: + +```html + +``` + +

(back to top)

+ +## Event System + +The core functionality of all comes from the simple and yet porweful event system wrapper for Inertia visit options, the `useForm()` uses the `useEventsSystem()` to create a way to access and retrive the event callbacks with multiple function calls correctly, without having to deal with object merging. + +The `useEventsSystem()` returns 4 functions `on()`, `combine()`, `execute()` and `toVisitOptions()`, the `on` function is just the same as its passed to the return of `useForm`, it also accepts these events `before`, `start`, `progress`, `success`, `error`, `cancel`, `finish`: + +```ts +const event = useEventsSystem() +event.on('before', (visit) => { + console.log(visit) +}) +``` + +As you can see it only gets `visit` as a parameter because `useEventsSystem()` was not specified that its events will receive more than that, but you can extend by passing an array of types of parameters to it: + +> We do that for `useForm()` so that the events also receive [`FormKitNode`](https://formkit.com/api-reference/formkit-core#formkitnode) from FormKit. + +```ts +const event = useEventsSystem<[node: FormKitNode]>() +event.on('before', (visit, node) => { + console.log(visit, node) +}) +``` + +The `combine()` function is meant to be used for easily pass multiple events in a single place: + +```ts +// addon.ts +return (on) => { + on('before', (visit, node) => { + console.log(visit, node) + }) + + on('success', (page, node) => { + console.log(page, node) + }) +} + +// app.ts +import addon from './addon' + +const event = useEventsSystem<[node: FormKitNode]>() +event.combine(addon) +``` + +The `execute()` function runs the events expects the event and the parameters to returns the expected return of that event callback from Inertia: + +```ts +const event = useEventsSystem<[node: FormKitNode]>() + +event.on('before', (visit, node) => { + console.log(visit, node) +}) +event.on('before', (visit, node) => { + return false +}) + +const result = event.execute('before', visit, node) // runs console.log +console.log(result) // returns false +``` + +The `toVisitOptions()` functions returns a `VisitOptions` with all events that where passed prior to it by wrapping the `execute()` function, and its already ready to used in Inertia's `router()` function: + +```ts +const event = useEventsSystem<[node: FormKitNode]>() + +event.on('before', (visit, node) => { + console.log(visit, node) +}) +event.on('start', (visit, node) => { + console.log(visit, node) +}) + +const options = event.toVisitOptions(node) +/** + * { + * onBefore: (visit) => { + * return execute('before', visit, node) + * }, + * onStart: (visit) => { + * return execute('start', visit, node) + * }, + * } + */ + +router.post('/login', options) +``` + +

(back to top)

+ +## Types + +
+ useForm + +```ts +const useForm: ( + initialFields?: F | undefined +) => { + on: < + T extends + | 'before' + | 'start' + | 'progress' + | 'finish' + | 'cancel' + | 'success' + | 'error' + | 'cancelToken' + >( + eventName: T, + callback: Events<[node: FormKitNode]>[T] + ) => void + combine: ( + combineCb: ( + cb: < + T extends + | 'before' + | 'start' + | 'progress' + | 'finish' + | 'cancel' + | 'success' + | 'error' + | 'cancelToken' + >( + eventName: T, + callback: Events<[node: FormKitNode]>[T] + ) => void + ) => + | void + | (( + cb: < + T extends + | 'before' + | 'start' + | 'progress' + | 'finish' + | 'cancel' + | 'success' + | 'error' + | 'cancelToken' + >( + eventName: T, + callback: Events<[node: FormKitNode]>[T] + ) => void + ) => void)[] + ) => void + plugin: (node: FormKitNode) => false | undefined + node: Ref + dirty: Ref + errors: Ref + processing: Ref + progress: Ref + recentlySuccessful: Ref + valid: Ref + wasSuccessful: Ref + get: ( + url: URL | string, + options?: Exclude + ) => (data: F, node: FormKitNode) => void + post: ( + url: URL | string, + options?: Exclude + ) => (data: F, node: FormKitNode) => void + put: ( + url: URL | string, + options?: Exclude + ) => (data: F, node: FormKitNode) => void + patch: ( + url: URL | string, + options?: Exclude + ) => (data: F, node: FormKitNode) => void + delete: ( + url: URL | string, + options?: Exclude + ) => (data: F, node: FormKitNode) => void + cancel: () => void +} +``` + +
+ +
+ Events + +```ts +export type Events = { + [K in keyof Omit]: ( + ...args: [...GlobalEventsMap[K]['parameters'], ...A] + ) => GlobalEventsMap[K]['result'] +} & { + cancelToken: (...args: [{ cancel: () => void }, ...A]) => void +} +``` + +
+ +
+ EventsList + +```ts +export type EventsList = { + [K in keyof Events]: Events[K][] +} +``` + +
+ + + +
+ useEventsSystem + +```ts +const useEventsSystem: () => { + on: < + T extends + | 'before' + | 'start' + | 'progress' + | 'finish' + | 'cancel' + | 'success' + | 'error' + | 'cancelToken' + >( + eventName: T, + callback: Events[T] + ) => void + combine: ( + combineCb: ( + cb: < + T extends + | 'before' + | 'start' + | 'progress' + | 'finish' + | 'cancel' + | 'success' + | 'error' + | 'cancelToken' + >( + eventName: T, + callback: Events[T] + ) => void + ) => + | void + | (( + cb: < + T extends + | 'before' + | 'start' + | 'progress' + | 'finish' + | 'cancel' + | 'success' + | 'error' + | 'cancelToken' + >( + eventName: T, + callback: Events[T] + ) => void + ) => void)[] + ) => void + execute: < + T extends + | 'before' + | 'start' + | 'progress' + | 'finish' + | 'cancel' + | 'success' + | 'error' + | 'cancelToken' + >( + eventName: T, + ...params: Parameters[T]> + ) => ReturnType[T]> | undefined + toVisitOptions: (...params: E) => VisitOptions +} +``` + +
+ +

(back to top)