Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Initial version of Lit adapter for Tanstack form #577

Merged
merged 51 commits into from
Mar 13, 2024
Merged
Show file tree
Hide file tree
Changes from 12 commits
Commits
Show all changes
51 commits
Select commit Hold shift + click to select a range
2a553e7
Initial Lit commit
Christian24 Jan 21, 2024
2b67b07
Fix tests
Christian24 Jan 21, 2024
ff7942c
Fix types
Christian24 Jan 22, 2024
69e867c
Remove unused packages
Christian24 Jan 22, 2024
8b70a3f
Remove unused packages
Christian24 Jan 22, 2024
57c33e0
Run prettier
Christian24 Jan 22, 2024
e3d2516
Removed tsup
Christian24 Jan 22, 2024
c5f4eb8
Change exports
Christian24 Jan 22, 2024
7e3a7af
Add simple example
Christian24 Jan 22, 2024
d9792cf
Prettier
Christian24 Jan 22, 2024
b488673
New example
Christian24 Jan 23, 2024
2f2c5f7
Add docs
Christian24 Jan 23, 2024
ad6b2e1
feat: support array notation in DeepKeys and DeepValue types
crutchcorn Jan 28, 2024
41afa97
fix: support numerical values for arrays in DeepValue better
crutchcorn Jan 28, 2024
87c3eac
feat!: remove form.Provider from React package
crutchcorn Jan 28, 2024
ee69c9b
chore: fix utils tests in regards to arrays
crutchcorn Jan 28, 2024
5f56e9c
feat!: remove form.Provider from Solid package
crutchcorn Jan 28, 2024
20803b8
chore!: proof of working arrays in react
crutchcorn Jan 28, 2024
c74fc86
Update docs/framework/lit/quick-start.md
Christian24 Feb 14, 2024
cfa5ab2
Update packages/lit-form/src/tanstack-form-controller.ts
Christian24 Feb 14, 2024
e1cc779
Update docs/framework/lit/quick-start.md
Christian24 Feb 14, 2024
bc50a5e
Add textarea
Christian24 Feb 18, 2024
c686d12
Merge remote-tracking branch 'origin/main'
Christian24 Feb 18, 2024
e7491f9
Merge branch 'main' into array-and-provider-refactor
crutchcorn Feb 29, 2024
acd30d9
Merge branch 'main' into array-and-provider-refactor
crutchcorn Mar 5, 2024
ce52a3a
chore: change Vue behavior and types
crutchcorn Mar 5, 2024
ce7c6bd
chore: fix CI, apply formatter, etc
crutchcorn Mar 5, 2024
2afe390
docs: remove mention of provider from docs
crutchcorn Mar 5, 2024
709167c
chore: add test to React array usage
crutchcorn Mar 5, 2024
9c2c73f
test: add test to Solid form
crutchcorn Mar 5, 2024
e3829ad
chore: fix Solid test
crutchcorn Mar 5, 2024
8302d49
chore: add array test to Vue
crutchcorn Mar 5, 2024
4468a46
chore: fix CI
crutchcorn Mar 5, 2024
ec09b38
docs: add array example to react docs
crutchcorn Mar 5, 2024
dfa5997
docs: improve the React basic concepts example
crutchcorn Mar 5, 2024
68c5375
chore: add React array example
crutchcorn Mar 5, 2024
cf623c0
docs: add SolidJS example and docs
crutchcorn Mar 5, 2024
be37e6b
chore: add Vue example
crutchcorn Mar 5, 2024
0f00a0e
docs: add array guide to Vue
crutchcorn Mar 5, 2024
6b3f58e
chore: fix CI
crutchcorn Mar 5, 2024
908d6f0
Merge branch 'main' into array-and-provider-refactor
crutchcorn Mar 9, 2024
c740617
chore: fix CI issues
crutchcorn Mar 9, 2024
5bbd418
Merge branch 'main' into Christian24/main
crutchcorn Mar 9, 2024
a413de3
Merge branch 'array-and-provider-refactor' into Christian24/main
crutchcorn Mar 9, 2024
f3f08a7
chore: fix CI
crutchcorn Mar 9, 2024
2bf8b70
chore: add back errorText to UI library demo
crutchcorn Mar 9, 2024
33da9df
chore: add back error validation in simple demo
crutchcorn Mar 9, 2024
fc217a2
chore: fix default value issues
crutchcorn Mar 9, 2024
ef30ec3
docs: remove mention of bind in quickstart docs
crutchcorn Mar 9, 2024
d71df41
docs: add to publish and docs
crutchcorn Mar 9, 2024
a561827
Merge branch 'main' into Christian24/main
crutchcorn Mar 13, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
105 changes: 105 additions & 0 deletions docs/framework/lit/quick-start.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
---
id: quick-start
title: Quick Start
---


The bare minimum to get started with TanStack Form is to create `FormOptions`and a `TanstackFormController`:

```ts
import type { FormOptions } from '@tanstack/lit-form'

interface Employee {
firstName: string
lastName: string
employed: boolean
jobTitle: string
}

interface Data {
employees: Partial<Employee>[]
}

const formConfig: FormOptions<Data> = {}

#form = new TanstackFormController(this, formConfig)
```

In this example `this`references to the instance of your `LitElement` in which you want to use Tanstack Form.
Christian24 marked this conversation as resolved.
Show resolved Hide resolved

To wire a form element in your template up with Tanstack Form simply use the `field` method of `TanstackFormController`:
```ts
render() {
return html`
<p>Please enter your first name></p>
${this.form.field(
{
name: `firstName`,
validators: {
onChange: ({ value }) =>
value.length < 3 ? 'Not long enough' : undefined,
},
},
(field: FieldApi<Employee, 'firstName'>) => {
return html` <div>
<label>First Name</label>
<input
id="firstName"
type="text"
placeholder="First Name"
@blur="${() => field.handleBlur()}"
.value="${field.getValue()}"
@input="${(event: InputEvent) => {
if (event.currentTarget) {
const newValue = (event.currentTarget as HTMLInputElement).value;
field.handleChange(newValue);
}
}}"
/>
</div>`
},
)}`;
}
```
The first parameter of `field` is `FieldOptions` and the second is callback to render your element. Be aware that you need
to handle updating the element and form yourself as seen in the example above. If you do not want to do this, you can use
the `bind` directive.

## The `bind` directive
Tanstack Form provides a `bind` directive which makes wiring up elements to the form a bit easier. You no longer need to
listen to events, update form state or component state, the `bind` directive will do it for you:
```ts
render() {
return html`
<p>Please enter your first name></p>
${this.form.field(
{
name: `firstName`,
validators: {
onChange: ({ value }) =>
value.length < 3 ? 'Not long enough' : undefined,
},
},
(field: FieldApi<Employee, 'firstName'>) => {
return html` <div>
<label>First Name</label>
<input
id="firstName"
type="text"
placeholder="First Name"
${bind(field)}
crutchcorn marked this conversation as resolved.
Show resolved Hide resolved
/>
</div>`
},
)}`;
}
```
The required parameter of the `bind` directive is the instance of `FieldApi` you want to bind to.
By default, the directive works for HTML's text inputs, checkbox inputs and selects.

If you want to make it work for custom
elements you can provide a function as an optional argument, which tells the directive how to wire up to your custom elements.
This function has the following signature: `<T extends HTMLElement, Value = any>(
element: T,
): ControlValueAccessor<T, Value> | undefined`. `ControlValueAccessor` is a provided interface that describes how Tanstack Form
Christian24 marked this conversation as resolved.
Show resolved Hide resolved
should interact with a given element. You can find an example of this in the `ui-libraries`-example.
15 changes: 15 additions & 0 deletions examples/lit/simple/.eslintrc.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// @ts-check

/** @type {import('eslint').Linter.Config} */
const config = {
extends: ['plugin:react/recommended', 'plugin:react-hooks/recommended'],
parserOptions: {
tsconfigRootDir: __dirname,
project: './tsconfig.json',
},
rules: {
'react/no-children-prop': 'off',
},
}

module.exports = config
27 changes: 27 additions & 0 deletions examples/lit/simple/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.

# dependencies
/node_modules
/.pnp
.pnp.js

# testing
/coverage

# production
/build

pnpm-lock.yaml
yarn.lock
package-lock.json

# misc
.DS_Store
.env.local
.env.development.local
.env.test.local
.env.production.local

npm-debug.log*
yarn-debug.log*
yarn-error.log*
6 changes: 6 additions & 0 deletions examples/lit/simple/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# Example

To run this example:

- `npm install`
- `npm run dev`
13 changes: 13 additions & 0 deletions examples/lit/simple/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vite + Lit + TS</title>
<script type="module" src="/src/index.ts"></script>
</head>
<body>
<tanstack-form-demo> </tanstack-form-demo>
</body>
</html>
30 changes: 30 additions & 0 deletions examples/lit/simple/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
{
"name": "@tanstack/form-example-lit-simple",
"version": "0.0.1",
"main": "src/index.jsx",
"scripts": {
"dev": "vite --port=3001",
"build": "vite build",
"preview": "vite preview",
"test:types": "tsc"
},
"dependencies": {
"@tanstack/lit-form": "<1.0.0",
"lit": "^3.1.1"
},
"devDependencies": {
"vite": "^5.0.10"
},
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
}
}
168 changes: 168 additions & 0 deletions examples/lit/simple/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
import { html, LitElement } from 'lit'
import { customElement } from 'lit/decorators.js'

import { styles } from './styles.js'
import type { FormOptions } from '@tanstack/lit-form'
import { TanstackFormController, bind } from '@tanstack/lit-form'

import { repeat } from 'lit/directives/repeat.js'

interface Employee {
firstName: string
lastName: string
employed: boolean
jobTitle: string
}

interface Data {
employees: Partial<Employee>[]
}

const formConfig: FormOptions<Data> = {}

@customElement('tanstack-form-demo')
export class TanstackFormDemo extends LitElement {
static styles = styles

#form = new TanstackFormController(this, formConfig)

render() {
return html`
<form
id="form"
@submit=${(e: Event) => {
e.preventDefault()
}}
>
<h1>Tanstack Form - Lit Demo</h1>
${this.#form.field(
{
name: 'employees',
defaultValue: [],
},
(employeesField) => {
return html`${repeat(
employeesField.getValue(),
(_, index) => index,
(_, index) => {
return html`
${this.#form.field(
{
name: `employees.${index}.firstName`,
validators: {
onChange: ({ value }: { value: string }) => {
return value && value.length < 3
? 'Not long enough'
: undefined
},
},
},
(field) => {
return html` <div>
<label>First Name</label>
<input
type="text"
placeholder="First Name"
${bind(field)}
/>
crutchcorn marked this conversation as resolved.
Show resolved Hide resolved
</div>`
},
)}
${this.#form.field(
{ name: `employees.${index}.lastName` },
(lastNameField) => {
return html` <div>
<label>Last Name</label>
<input
type="text"
placeholder="Last Name"
${bind(lastNameField)}
/>
</div>`
},
)}
${this.#form.field(
{ name: `employees.${index}.employed` },
(employedField) => {
return html`<div>
<label>Employed?</label>
<input
type="checkbox"
@input="${() =>
employedField.handleChange(
!employedField.getValue(),
)}"
.checked="${employedField.getValue()}"
@blur="${() => employedField.handleBlur()}"
/>
</div>
${employedField.getValue()
? this.#form.field(
{
name: `employees.${index}.jobTitle`,
validators: {
onChange: ({
value,
}: {
value: string
}) => {
return value.length === 0
? 'Needs to have a job here'
: null
},
},
},
(jobTitleField) => {
return html` <div>
<label>Job Title</label>
<input
type="text"
placeholder="Job Title"
${bind(jobTitleField)}
/>
</div>`
},
)
: ''} `
},
)}
`
},
)}

<div>
<button
type="button"
@click=${() => {
employeesField.pushValue({
firstName: '',
lastName: '',
employed: false,
})
}}
>
Add employee
</button>
</div> `
},
)}

<div>
<button type="submit" ?disabled=${this.#form.api.state.isSubmitting}>
${this.#form.api.state.isSubmitting ? html` Submitting` : 'Submit'}
</button>
<button
type="button"
id="reset"
@click=${() => {
this.#form.api.reset()
}}
>
Reset
</button>
</div>
</form>
<pre>${JSON.stringify(this.#form.api.state, null, 2)}</pre>
`
}
}
Loading