From 04c6f138e5fd68021d0765f1f089aba8f52eddc2 Mon Sep 17 00:00:00 2001 From: Maurice de Bruyn Date: Sun, 7 Apr 2024 14:39:49 +0200 Subject: [PATCH 1/2] docs(doc-site): improve documentation --- docs/guide/array-fields.md | 3 +-- docs/guide/basic-usage.md | 15 +++++------ docs/guide/concepts.md | 37 +++++++++----------------- docs/guide/dynamic-objects.md | 3 +-- docs/guide/quickstart.md | 8 +----- docs/guide/react/basic-usage.md | 21 +++++---------- docs/guide/react/quickstart.md | 3 +-- docs/guide/validation.md | 29 ++++++++------------ docs/guide/what-are-signals.md | 41 +++++++++-------------------- docs/reference/core/FieldLogic.md | 3 +-- docs/reference/core/FormLogic.md | 3 +-- docs/reference/react/FormContext.md | 3 +-- 12 files changed, 56 insertions(+), 113 deletions(-) diff --git a/docs/guide/array-fields.md b/docs/guide/array-fields.md index fca4af7..40ac756 100644 --- a/docs/guide/array-fields.md +++ b/docs/guide/array-fields.md @@ -1,7 +1,6 @@ # Array Fields -In many cases using arrays in forms is necessary and hard to manage. -You need to keep of the changes to each item and the array itself to know if an item was added, removed or swapped. +In many cases, using arrays in forms is necessary but hard to manage. You need to keep track of the changes to each item and the array itself to know if an item was added, removed, or swapped. ## Data Representation diff --git a/docs/guide/basic-usage.md b/docs/guide/basic-usage.md index ba1b191..6b9711f 100644 --- a/docs/guide/basic-usage.md +++ b/docs/guide/basic-usage.md @@ -60,7 +60,7 @@ field.data.value = "New Name" ## Form Submission A form is not complete without a submit-handler. -To add one, add the onSubmit method to the form options. +To add one, add the `onSubmit` method to the form options. ```ts const form = new FormLogic({ @@ -124,8 +124,9 @@ to convert the error to a valid format. ## Accessing Data -There are several different ways to access the form data. -Your choice will depend on the use case. +There are several ways to access form data in this library, each suited for different scenarios. + +We'll explore these methods and discuss when each approach might be most beneficial. ### Through Fields @@ -196,13 +197,11 @@ you will subscribe to every change within the form data. ## Add Validation -A crucial part of forms is validation. -You can either add validation to the whole form or to a single field. +Validation is essential for forms as it safeguards data integrity and provides a smooth user experience. This library offers two approaches to validation: form-level and field-level. ### Field Validation -Adding validation to a single field might be the simplest and most common way to validate a form. -You can add a validation function to the field options. +Adding validation rules directly to individual fields is a common and straightforward approach. This library allows you to define a validation function within the field options. ```ts const field = form.getOrCreateField("name", { @@ -276,7 +275,7 @@ please refer to the [Validation Guide](/guide/validation). ## Add Transformation -In many cases, you have data in your form that has a type which cannot be used by your application. +In many cases, you have data in your form that has a type incompatible with your application. A common example is numbers, which are stored as such in the form, but are needed as string in the application (e.g., in an input field). The users of the transformed values are called bindings in this context. diff --git a/docs/guide/concepts.md b/docs/guide/concepts.md index 6ea348b..a2a975a 100644 --- a/docs/guide/concepts.md +++ b/docs/guide/concepts.md @@ -5,20 +5,13 @@ let's take a look at what to keep in mind when working with the core components ## Form -A form is the main part of the library. -It is the single source of truth for the form data, -it holds all the references to the data of each field and the data that is not controlled by a field. +The form is the core component of this library. It acts as **the single source of truth** for all form data. It holds references to both the data of each field and any data not controlled by a specific field. -Besides owning the data, -the form is also alone responsible for the submission of the form data -and can handle validation on the complete form data. +In addition to managing the data, the form is solely responsible for submitting form data and can handle validation for the complete form data. -With these functionalities the form is able to work without the need for any fields. -All the core functionalities can be accessed through the form instance. +These functionalities allow the form to function independently of any fields. All core features can be accessed through the form instance itself. -Nested data will be stored as nested signals, -that way the form can guarantee that each change to a value will only affect subscribers to the child value. -You would access nested data like this: +Nested data is stored using nested signals. This approach ensures that any changes to a value only affect subscribers to the specific child value. You would access nested data in the following way: ```ts import {FormLogic} from "./FormLogic"; @@ -41,26 +34,20 @@ Only use the `.value` prop if you want to subscribe to changes of that value, ot ## Field -A field is a wrapper around a single piece of data within the form. +A field acts as a wrapper for a single piece of data within the form. It is responsible for storing field-specific state and handling logic like validation and transformation. -Fields are meant -to be used as a way to ease the interaction with the form -and reduce the amount of long chaining for forms with deeply nested data. +Fields are designed to simplify interaction with the form and reduce the need for long method chaining when working with forms containing deeply nested data. -Once a field is created, it takes control of the data assigned to it. -It does not store the data itself but rather a reference to the data in the form. -The field value is now connected to the field lifecycle and will be removed from the form when the field is removed. -This behavior can be configured, but by default, the field will destroy the connected data when it is unmounted. +When you create a field, it takes control of the referenced data within the form. It doesn't store the data directly but rather a reference to it. This connects the field value to the field's lifecycle. Any data associated with the field will be removed from the form when the field itself is removed. This behavior can be customized, but by default, the field will delete the referenced data when it's unmounted. ## Arrays -Arrays are mostly handled like every other nested field, -it can be accessed through its path string, and every value is wrapped in a signal. -Most UI libraries, however, need some sort of unique identifier for items in an array -to keep track of whether an item is added, -removed or moved. -For that reason this library adds a unique `key` identifier to each array signal created. +Arrays are mostly handled like every other nested field. +It can be accessed through its path string, and every value is wrapped in a signal. +However, most UI libraries require unique identifiers for array items +to track whether an item is added, removed or moved. +To address this, this library automatically adds a unique `key` identifier to each array signal. So be aware when using arrays and keep in mind, that the raw data has to be accessed through the `.data` prop. diff --git a/docs/guide/dynamic-objects.md b/docs/guide/dynamic-objects.md index 6f26259..44b9f89 100644 --- a/docs/guide/dynamic-objects.md +++ b/docs/guide/dynamic-objects.md @@ -1,7 +1,6 @@ # Dymamic Objects -In some cases, you may want to have a dynamic object structure, where the keys are not known in advance. -This library handles those scenarios as if they were static objects, but exposes some helper functions +In some cases, you may want to have a dynamic object structure, where the keys are not known in advance. This library handles these scenarios as if they were static objects, but exposes some helper functions for working with them. ## Helper Functions diff --git a/docs/guide/quickstart.md b/docs/guide/quickstart.md index 80b940f..b137ca6 100644 --- a/docs/guide/quickstart.md +++ b/docs/guide/quickstart.md @@ -40,13 +40,7 @@ If you want to use a schema validation library you can also install the correspo ## Creating your first form -This is how you would create a simple registration form with a name and email field would look like. -This example also includes validation using the Zod library. - -In a real world project, you would then probably connect the state of the form to a UI, this is not covered in this -example. -Check out the [React quickstart guide](/guide/react/quickstart#creating-your-first-form) for an example with the React -bindings. +This is what a simple registration form with a name and email field would look like. This example also includes validation using the Zod library. Connecting the form state to a UI is not covered here. For an example with React bindings, check out the [React quickstart guide](/guide/react/quickstart#creating-your-first-form). ::: code-group diff --git a/docs/guide/react/basic-usage.md b/docs/guide/react/basic-usage.md index 418d888..def9a17 100644 --- a/docs/guide/react/basic-usage.md +++ b/docs/guide/react/basic-usage.md @@ -61,9 +61,7 @@ export default function MyForm() { ``` ::: tip -It is recommended -to create your custom `input` component that takes the field signal as a prop to optimize the re-renders. -If you use the `nameField.data.value` in the main component, it will re-render the whole component on every change. +To optimize re-renders, create a custom `input` component that receives the field signal as a prop. Using `nameField.data.value` directly in the main component triggers a full re-render on every change. ::: ::: info @@ -93,9 +91,9 @@ export default function MyForm() { ``` ::: danger WARNING -You cannot subscribe to a signal value from within an arrow function. -So you will not be able to use `field.data.value` directly in the main component, -but rather have to create a child component that subscribes to the signal. +You cannot subscribe to a signal value within an arrow function. +This means using `field.data.value` directly in the main component is not possible. +To access the signal value, you'll need to create a child component that subscribes to the signal. ::: If you want to avoid this pitfall, you can use the `FieldProvider` component and consume the fields context in a child @@ -129,7 +127,7 @@ export default function MyForm() { The basic principles of form submission are the same as in the core library. -It is recommended to use a default html `form` element to wrap your form and hook into the `onSubmit` event. +It is recommended to use a default HTML `form` element to wrap your form and hook into the `onSubmit` event. ```tsx {10-13,17-18} export default function MyForm() { @@ -159,8 +157,7 @@ export default function MyForm() { The basic principles of accessing data are the same as in the core library. -Additionally, it is important to note, that you can always use the `useFormContext` and `useFieldContext` hooks to -access the form and field data. +Additionally, you can always use the `useFormContext` and `useFieldContext` hooks to access form and field data directly. ::: info You can only use these hooks from children of the `FormProvider` or `FieldProvider` components. @@ -191,11 +188,7 @@ export default function MyForm() { ## Add Transformation -The basic principles of adding transformation are the same as in the core library. - -So adding transformation to a field is as simple -as adding the `transformFromBinding` and `transformToBinding` options to the field -and using the `transformedData` property of the field. +The basic principles of transforming data are the same as in the core library. To add a transformation to a field, use the `transformFromBinding` and `transformToBinding` options. You can then access the transformed data using the `transformedData` property of the field. ```tsx {8-9,12} export default function MyForm() { diff --git a/docs/guide/react/quickstart.md b/docs/guide/react/quickstart.md index ab9eba2..5f2f271 100644 --- a/docs/guide/react/quickstart.md +++ b/docs/guide/react/quickstart.md @@ -45,8 +45,7 @@ If you want to use a schema validation library you can also install the correspo ## Creating your first form -This is how you would create a simple registration form with a name and email field would look like. -This example also includes validation using the Zod library. +This code snippet demonstrates a simple registration form with `name`, `email`, and `password` fields. The example utilizes Zod for validation. ::: tip Just like in this example it is advised to create your own input components, that accept signals as props. diff --git a/docs/guide/validation.md b/docs/guide/validation.md index 6acf461..5c207fa 100644 --- a/docs/guide/validation.md +++ b/docs/guide/validation.md @@ -51,11 +51,10 @@ It is not possible to disable the validation on submit. ## Async Validation -This library also allows for async validation by default. -To do that, pass an asynchronous function to the `validatorAsync` option. -It follows the same rules as the synchronous validation, but instead of returning a string, -it returns a promise that resolves to a string or `undefined`. -It also receives an `AbortSignal` as a second argument, which can be used to cancel the validation. +This library also supports asynchronous validation out of the box. +To enable it, pass an asynchronous function to the `validatorAsync` option. +It follows the same rules as synchronous validation, but instead of returning a `string` directly, it returns a promise that resolves to either a `string` or `undefined`. +The asynchronous function also receives an `AbortSignal` as a second argument, which allows you to cancel the validation in progress. If the signal is aborted, the validation is considered canceled and all errors are discarded. ::: info @@ -113,19 +112,19 @@ const form = new FormLogic({ ``` ::: info -The abortSignal of the validator is also aborted if there is a new debounced validation. +The `abortSignal` of the `validator` is also aborted if there is a new debounced validation. That means, with this example, you will only see the error message after 1500ms after the last validation. ::: ## Deep Validation -As mentioned before, the form will run onChange validation on every nested change. +As mentioned before, the form will run `onChange` validation on every nested change. Fields, however, only run validation if the direct value is changed. -So if you have a field that has an array value, it only runs validation if elements are added, removed or swapped. -No validation will be run if the value of an array item is changed. +So, if you have a field that has an array value, it only runs validation if elements are added, removed or swapped. +No validation is run if the value of an item within the array is changed. This library allows you to listen to those deep changes -and trigger an onChange validation on a parent if a nested value changes. +and trigger an `onChange` validation on a parent if a nested value changes. To do that, you can set the `validateOnNestedChange` option to `true` when creating a new field. ```ts @@ -154,15 +153,9 @@ So even a change 5 levels deep will trigger the validation of the parent. ## Validation Mixins -Sometimes you have to validate a field relative to the value of another field. -Many libraries struggle with this issue and usually require you to have validation on a common parent. -This library solves the issue with validation mixins, -which allow you to add any other value from the form to the validation function. +Sometimes, you have to validate a field relative to the value of another field. Many libraries struggle with this issue and usually require you to have validation on a common parent. This library solves the issue with validation mixins, which allow you to add any other value from the form to the validation function. -To do that, add the paths as an array to the `validateMixin` option when creating a new field. -This will transform the input of the validator to a tuple, -where the first value is the value of the field, -and the other values are the values provided by the paths in the same order. +To do that, add the paths as an array to the `validateMixin` option when creating a new field. This will transform the input of the validator to a tuple, where the first value is the value of the field, and the other values are the values provided by the paths in the same order. ```ts import {FormLogic} from '@formsignals/form-core'; diff --git a/docs/guide/what-are-signals.md b/docs/guide/what-are-signals.md index 4f41751..19aa0c5 100644 --- a/docs/guide/what-are-signals.md +++ b/docs/guide/what-are-signals.md @@ -8,35 +8,23 @@ description: An introduction to Signals and why they are used for Form Signals. In short, Signals are reactive values that can be used to trigger UI updates or calculate computed data based on changes. -There are a few frontend libraries, that use Signals as their state management solution, such -as [Svelte V5](https://svelte.dev/blog/runes), [SolidJS](https://www.solidjs.com/tutorial/introduction_signals), [Angular](https://angular.io/guide/signals), [Qwik](https://qwik.dev/docs/components/state/), [Preact](https://preactjs.com/guide/v10/signals/) -and possibly many more. -Signals are not only a concept used for UI libraries, but can also be used in default javascript code to create reactive -values. -The Preact team has built a great solution and exposing their Signals API to the public and even created bindings for +Several frontend libraries use Signals as their state management solution, including [Svelte V5](https://svelte.dev/blog/runes), [SolidJS](https://www.solidjs.com/tutorial/introduction_signals), [Angular](https://angular.io/guide/signals), [Qwik](https://qwik.dev/docs/components/state/), [Preact](https://preactjs.com/guide/v10/signals/) +(and possibly many more). +Signals are not just a concept for; they can also be used in vanilla JavaScript code to create reactive values. +The Preact team has built a great solution by exposing their Signals API to the public and even created bindings for some other libraries like React. Form Signals is built on top of the Preact Signals API and uses it to manage the form state. For more information, check -out the Preact teams [documentation](https://preactjs.com/guide/v10/signals/). +out the Preact team's [documentation](https://preactjs.com/guide/v10/signals/). ## Why Signals? -When it comes to complex forms, you usually want to have less re-renders and only re-render to components that have to, -to display the current state of the form. -Signals are a great way to achieve this, as they only re-render the components that are subscribed to the Signal. -This way, you can have a complex form with many fields and only re-render the fields that have changed. +When it comes to complex forms, you usually want to minimize re-renders and only update components that need to display the current state. Signals achieve this by only re-rendering components subscribed to the Signal that changed. +This way, even complex forms with many fields will only update the specific fields that have changed, leading to a smoother and faster user experience. -Preact Signals also have a great concept of computed values, which can be used to calculate a value based on other -Signals. -When it comes to forms, there are several state values that can be calculated based on other state values, such as -whether a field is dirty. -Computed values are only ever re-calculated when dependent Signals change and only re-render components that are -subscribed if the actual return value of the computed value has changed. +Preact Signals also offer a powerful feature: computed values. These allow calculating a value based on other Signals. When it comes to forms, a common use case is determining whether a field is dirty. Computed values are only re-calculated when the Signals they depend on change, and only components subscribed to the computed value re-render if its actual return value has changed. -Signals often times use an easy-to-understand API, where changes to a Signal can be done by setting a new value to the -Signal. -That way there is no need for an additional function call, and the new values are directly applied to the Signal and -available in later calls to the signal. +Signals often use an easy-to-understand API. Changes to a Signal are made simply by setting a new value to it. This eliminates the need for an additional function call, and the new values are directly applied and available in subsequent calls to the Signal. ## Basic Example of Signals @@ -58,15 +46,10 @@ counterSignal.value++; console.log(doubleCounter.value); // 2 ``` -Now every time the `counterSignal` changes, the `doubleCounter` signal will be re-calculated. -If there are no listeners to the `doubleCounter` signal, the value will never be re-calculated, since it is lazy. +Whenever the `counterSignal` changes, the `doubleCounter` signal will be re-calculated to reflect the updated value. However, `doubleCounter` is lazy. This means it only calculates its value (double the value of `counterSignal`) when it's actually needed by a component or another signal that has subscribed to it. This lazy evaluation helps improve performance by avoiding unnecessary calculations when the value of `doubleCounter` isn't being used. ## Signals Reactivity -Signals are reactive by default, so every time a signals value is accessed with the `.value` property the value of the -signal is retrieved reactively. -That means, that if this done from within a component, or a `computed` or an `effect`, every change in the value will -case an update. +Signals are reactive by default. So, whenever a signal's value is accessed with the `.value` property, the value is retrieved reactively. This means that if this is done within a component, a `computed` value, or an `effect`, any change in the value will cause an update. -In many cases where you do not want to explicitly subscribe to the changes (for example in click handlers), you can use -the `.peek()` method to get the current value without subscribing to the signal. +In some cases, you might not need to subscribe to a signal for updates (e.g., within click handlers). For these scenarios, Preact Signals offer the `.peek()` method. This method allows you to get the current value of the signal without subscribing to it, avoiding unnecessary re-renders. diff --git a/docs/reference/core/FieldLogic.md b/docs/reference/core/FieldLogic.md index 90225af..0f8b755 100644 --- a/docs/reference/core/FieldLogic.md +++ b/docs/reference/core/FieldLogic.md @@ -262,8 +262,7 @@ interface FieldLogicHandlers< The field offers several helpers to interact with [arrays](/guide/array-fields) and [dynamic objects](/guide/dynamic-objects). -Some of these types might look complex, but they are necessary to ensure, -that you only can insert, swap or remove at the correct positions. +Some of these types might look complex, but they are necessary to ensure that you only can insert, swap or remove at the correct positions. E.g., you can only swap two values in an array if they are of the same type, or you can never insert into a tuple ```ts diff --git a/docs/reference/core/FormLogic.md b/docs/reference/core/FormLogic.md index 4f2082f..ab6d190 100644 --- a/docs/reference/core/FormLogic.md +++ b/docs/reference/core/FormLogic.md @@ -258,8 +258,7 @@ interface FormLogic< The form offers several helpers to interact with [arrays](/guide/array-fields) and [dynamic objects](/guide/dynamic-objects). -Some of these types might look complex, but they are necessary to ensure, -that you only can insert, swap or remove at the correct positions. +Some of these types might look complex, but they are necessary to ensure that you only can insert, swap or remove at the correct positions. E.g., you can only swap two values in an array if they are of the same type, or you can never insert into a tuple ```ts diff --git a/docs/reference/react/FormContext.md b/docs/reference/react/FormContext.md index b59ce6e..c3308f9 100644 --- a/docs/reference/react/FormContext.md +++ b/docs/reference/react/FormContext.md @@ -41,8 +41,7 @@ interface FormContextType< | `handleSubmitOnEnter` | A function that can be used capture the enter key press on an HTML element, stop the events propagation and run the `handleSubmit` method of the form. | ::: info -The `handleSubmitOnEnter` can be useful if you have a form that has no HTML form element, or is a subform within an HTML form. -You cannot nest HTML form elements, so the browser functionality to submit a form on entering will not work unless you use this function on a wrapping container of the subform elements. +The `handleSubmitOnEnter` function can be useful for forms that lack a standard HTML form element, or for subforms within a larger HTML form. Since HTML forms cannot be nested, the browser's built-in functionality to submit on Enter won't work. To address this, use `handleSubmitOnEnter` on the container element that wraps your subform's elements. ::: ## formLogicToFormContext From 9b5b53fbe68fe303d89f0d7adaaaa7524cfbd66d Mon Sep 17 00:00:00 2001 From: Maurice de Bruyn Date: Mon, 8 Apr 2024 19:26:36 +0200 Subject: [PATCH 2/2] docs(doc-site): fix sentence --- docs/guide/what-are-signals.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/guide/what-are-signals.md b/docs/guide/what-are-signals.md index 19aa0c5..4dc3d55 100644 --- a/docs/guide/what-are-signals.md +++ b/docs/guide/what-are-signals.md @@ -10,7 +10,7 @@ changes. Several frontend libraries use Signals as their state management solution, including [Svelte V5](https://svelte.dev/blog/runes), [SolidJS](https://www.solidjs.com/tutorial/introduction_signals), [Angular](https://angular.io/guide/signals), [Qwik](https://qwik.dev/docs/components/state/), [Preact](https://preactjs.com/guide/v10/signals/) (and possibly many more). -Signals are not just a concept for; they can also be used in vanilla JavaScript code to create reactive values. +Signals are not just a concept used for UI libraries; they can also be used in vanilla JavaScript code to create reactive values. The Preact team has built a great solution by exposing their Signals API to the public and even created bindings for some other libraries like React.