diff --git a/LICENSE b/LICENSE old mode 100644 new mode 100755 diff --git a/codecov.yml b/codecov.yml old mode 100644 new mode 100755 diff --git a/docs/config.json b/docs/config.json index f1c6e71fe..04a82da3c 100644 --- a/docs/config.json +++ b/docs/config.json @@ -197,6 +197,19 @@ "to": "framework/solid/guides/linked-fields" } ] + }, + { + "label": "lit", + "children": [ + { + "label": "Basic Concepts", + "to": "framework/lit/guides/basic-concepts" + }, + { + "label": "Arrays", + "to": "framework/lit/guides/arrays" + } + ] } ] }, @@ -385,7 +398,7 @@ }, { "label": "Classes / TanStackFormController", - "to": "framework/lit/reference/classes/tanstackformcontroller" + "to": "framework/lit/reference/tanstackformcontroller" } ] }, diff --git a/docs/framework/lit/guides/arrays.md b/docs/framework/lit/guides/arrays.md new file mode 100644 index 000000000..3bbf86da8 --- /dev/null +++ b/docs/framework/lit/guides/arrays.md @@ -0,0 +1,181 @@ +--- +id: arrays +title: Arrays +--- + +TanStack Form supports arrays as values in a form, including sub-object values inside of an array. + +# Basic Usage + +To use an array, you can use `field.state.value` on an array value, as in: + +```ts +export class TestForm extends LitElement { + #form = new TanStackFormController(this, { + defaultValues: { + people: [] as Array<{ name: string; age: string }>, + }, + }) + render() { + return html` +
{ + e.preventDefault() + }} + > +

Please enter your details

+ ${this.#form.field( + { + name: `people`, + }, + (peopleField) => { + return html`${repeat( + peopleField.state.value, + (_, index) => index, + (_, index) => { + return html` // ... ` + }, + )} ` + }, + )} +
+ ` + } +} +``` + +This will generate the mapped HTML every time you run pushValue on the field: + + +```html +
+ +
+``` + +Finally, you can use a subfield like so: + +```ts +return html` + ${this.#form.field( + { + name: `people[${index}].name`, + }, + (field) => { + return html` + + `; + }, + )} + ` +``` + + +## Full Example + + +```typescript +export class TestForm extends LitElement { + #form = new TanStackFormController(this, { + defaultValues: { + people: [] as Array<{ name: string}>, + }, + }); + render() { + return html` +
{ + e.preventDefault(); + }} + > +

Please enter your details

+ ${this.#form.field( + { + name: `people`, + }, + (peopleField) => { + return html`${repeat( + peopleField.state.value, + (_, index) => index, + (_, index) => { + return html` + ${this.#form.field( + { + name: `people[${index}].name`, + }, + (field) => { + return html`
+
+ + +
+
`; + } + )} + `; + } + )} + +
+ +
`; + } + )} + +
+ + +
+
+ `; + } + +declare global { + interface HTMLElementTagNameMap { + "test-form": TestForm; + } +} +``` diff --git a/docs/framework/lit/guides/basic-concepts.md b/docs/framework/lit/guides/basic-concepts.md new file mode 100644 index 000000000..0c6e354f9 --- /dev/null +++ b/docs/framework/lit/guides/basic-concepts.md @@ -0,0 +1,99 @@ +--- +id: basic-concepts +title: Basic Concepts and Terminology +--- + +This page introduces the basic concepts and terminology used in the `@tanstack/lit-form` library. Familiarizing yourself with these concepts will help you better understand and work with the library and its usage with Lit. + +## Form Options + +You can create options for your form so that it can be shared between multiple forms by using the `formOptions` function. + +For Example: + +```tsx +const formOpts = formOptions({ + defaultValues: { + firstName: '', + lastName: '', + employed: false, + jobTitle: '', + } as Employee, +}) +``` + +## Form Instance + +A Form Instance is an object that represents an individual form and provides methods and properties for working with the form. You create a form instance using the `TanStackFormController` interface provided by `@tanstack/lit-form`. The `TanStackFormController` is instantiated with the current form's (`this`) class and some default form options. It initializes the form state, handles form submission, and provides methods to manage form fields and their validation. + +```tsx +#form = new TanStackFormController(this, { + defaultValues: { + firstName: '', + lastName: '', + employed: false, + jobTitle: '', + } as Employee, +}) +``` + +You may also create a form instance without using `formOptions` by using the standalone `TanStackFormController` API: + +```tsx +#form = new TanStackFormController(this, { + ...formOpts, +}) +``` + +## Field + +A Field represents a single form input element, such as a text input or a checkbox. Fields are created using the `field(FieldOptions, callback)` provided by the form instance. The component accepts a `FieldOptions` object and a callback function that receives a `FieldApi` object. This object provides methods to get the current value of the field, handle input changes, and handle blur events. + +For Example: + +```ts + ${this.#form.field( + { + name: `firstName`, + validators: { + onChange: ({ value }) => + value.length < 3 ? "Not long enough" : undefined, + }, + }, + (field: FieldApi) => { + return html`
+ + +
`; + }, +)} +``` + +## Field State + +Each field has its own state, which includes its current value, validation status, error messages, and other metadata. You can access a field's state using its `field.state` property. + +```tsx +const { value, meta: { errors, isValidating } } = field.state +``` + +There are three field states can be very useful to see how the user interacts with a field. A field is _"touched"_ when the user clicks/tabs into it, _"pristine"_ until the user changes value in it, and _"dirty"_ after the value has been changed. You can check these states via the `isTouched`, `isPristine` and `isDirty` flags, as seen below. + +```tsx +const { isTouched, isPristine, isDirty } = field.state.meta +``` + +![Field states](https://raw.githubusercontent.com/TanStack/form/main/docs/assets/field-states.png) diff --git a/docs/framework/lit/quick-start.md b/docs/framework/lit/quick-start.md index 78345f778..6f19510ad 100644 --- a/docs/framework/lit/quick-start.md +++ b/docs/framework/lit/quick-start.md @@ -3,8 +3,7 @@ id: quick-start title: Quick Start --- - -The bare minimum to get started with TanStack Form is to create a `TanstackFormController`: +The bare minimum to get started with TanStack Form is to create a `TanstackFormController` as seen below with the `Employee` interface for our test form: ```ts interface Employee { @@ -14,52 +13,71 @@ interface Employee { jobTitle: string } -#form = new TanstackFormController(this, { +#form = new TanStackFormController()(this, { defaultValues: { - employees: [] as Employee[], + firstName: '', + lastName: '', + employed: false, + jobTitle: '', }, }) ``` In this example `this` references the instance of your `LitElement` in which you want to use TanStack Form. -To wire a form element in your template up with TanStack Form, use the `field` method of `TanstackFormController`: +To wire a form element in your template up with TanStack Form, use the `field` method of `TanstackFormController`. + +The first parameter of `field` is `FieldOptions` and the second is callback to render your element. + +```ts +field(FieldOptions, callback) +``` + +Our completed test form should look something like below. The form collects first name from a user input field: ```ts export class TestForm extends LitElement { + #form = new TanStackFormController(this, { + defaultValues: { + firstName: '', + lastName: '', + employed: false, + jobTitle: '', + }, + }) render() { - return html` -

Please enter your first name>

- ${this.form.field( - { - name: `firstName`, - validators: { - onChange: ({ value }) => - value.length < 3 ? 'Not long enough' : undefined, + return html`

Please enter your first name>

+ ${this.#form.field( + { + name: `firstName`, + validators: { + onChange: ({ value }) => + value.length < 3 ? 'Not long enough' : undefined, + }, + }, + (field: FieldApi) => { + return html`
+ + +
` }, - }, - (field: FieldApi) => { - return html`
- - -
` - }, - )}`; + )}` } } ``` -The first parameter of `field` is `FieldOptions` and the second is callback to render your element. Be aware that you need +Be aware that you need to handle updating the element and form yourself as seen in the example above. diff --git a/knip.json b/knip.json old mode 100644 new mode 100755 diff --git a/nx.json b/nx.json old mode 100644 new mode 100755 diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml old mode 100644 new mode 100755