@samline/forms 1.0.3 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +87 -199
- package/dist/browser/global.d.ts +152 -0
- package/dist/browser/global.global.js +778 -474
- package/dist/browser/global.global.js.map +1 -1
- package/dist/index.cjs +753 -470
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +141 -3
- package/dist/index.d.ts +141 -3
- package/dist/index.js +960 -13
- package/dist/index.js.map +1 -1
- package/package.json +8 -43
- package/dist/chunk-4NEGCRY2.js +0 -125
- package/dist/chunk-4NEGCRY2.js.map +0 -1
- package/dist/chunk-PCKGH2WA.js +0 -11
- package/dist/chunk-PCKGH2WA.js.map +0 -1
- package/dist/chunk-WM2CL7IO.js +0 -576
- package/dist/chunk-WM2CL7IO.js.map +0 -1
- package/dist/core/index.cjs +0 -154
- package/dist/core/index.cjs.map +0 -1
- package/dist/core/index.d.cts +0 -11
- package/dist/core/index.d.ts +0 -11
- package/dist/core/index.js +0 -13
- package/dist/core/index.js.map +0 -1
- package/dist/react/index.cjs +0 -734
- package/dist/react/index.cjs.map +0 -1
- package/dist/react/index.d.cts +0 -10
- package/dist/react/index.d.ts +0 -10
- package/dist/react/index.js +0 -43
- package/dist/react/index.js.map +0 -1
- package/dist/svelte/index.cjs +0 -764
- package/dist/svelte/index.cjs.map +0 -1
- package/dist/svelte/index.d.cts +0 -20
- package/dist/svelte/index.d.ts +0 -20
- package/dist/svelte/index.js +0 -72
- package/dist/svelte/index.js.map +0 -1
- package/dist/types-jYB7Wp7C.d.cts +0 -95
- package/dist/types-jYB7Wp7C.d.ts +0 -95
- package/dist/vanilla/index.cjs +0 -705
- package/dist/vanilla/index.cjs.map +0 -1
- package/dist/vanilla/index.d.cts +0 -7
- package/dist/vanilla/index.d.ts +0 -7
- package/dist/vanilla/index.js +0 -12
- package/dist/vanilla/index.js.map +0 -1
- package/dist/vue/index.cjs +0 -741
- package/dist/vue/index.cjs.map +0 -1
- package/dist/vue/index.d.cts +0 -13
- package/dist/vue/index.d.ts +0 -13
- package/dist/vue/index.js +0 -56
- package/dist/vue/index.js.map +0 -1
package/README.md
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
# Forms
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
> A small, framework-free form controller for vanilla JS and direct browser usage.
|
|
4
|
+
|
|
5
|
+
> It binds to an `HTMLFormElement`, keeps field state in sync with the DOM, runs validation, lets you react to changes via watchers or subscribers, and ships a serialized payload for `fetch` flows.
|
|
6
|
+
|
|
7
|
+
---
|
|
4
8
|
|
|
5
9
|
## Table of Contents
|
|
6
10
|
|
|
@@ -8,12 +12,13 @@ Form controller for native HTML forms, React, Vue, Svelte, and direct browser us
|
|
|
8
12
|
- [CDN / Browser](#cdn--browser)
|
|
9
13
|
- [Entrypoints](#entrypoints)
|
|
10
14
|
- [Quick Start](#quick-start)
|
|
11
|
-
- [What You Can
|
|
12
|
-
- [API
|
|
13
|
-
- [Examples](#examples)
|
|
15
|
+
- [What You Can Build](#what-you-can-build)
|
|
16
|
+
- [API at a Glance](#api-at-a-glance)
|
|
14
17
|
- [Documentation](#documentation)
|
|
15
18
|
- [License](#license)
|
|
16
19
|
|
|
20
|
+
---
|
|
21
|
+
|
|
17
22
|
## Installation
|
|
18
23
|
|
|
19
24
|
```bash
|
|
@@ -24,49 +29,58 @@ npm install @samline/forms
|
|
|
24
29
|
pnpm add @samline/forms
|
|
25
30
|
```
|
|
26
31
|
|
|
27
|
-
```bash
|
|
28
|
-
yarn add @samline/forms
|
|
29
|
-
```
|
|
30
|
-
|
|
31
32
|
```bash
|
|
32
33
|
bun add @samline/forms
|
|
33
34
|
```
|
|
34
35
|
|
|
36
|
+
Requires Node 20+ when bundling. Runtime target is ES2020.
|
|
37
|
+
|
|
38
|
+
---
|
|
39
|
+
|
|
35
40
|
## CDN / Browser
|
|
36
41
|
|
|
37
|
-
Use the browser build when you do not have a bundler and need to run the package directly in HTML.
|
|
42
|
+
Use the browser build when you do not have a bundler and need to run the package directly in HTML, Shopify, WordPress, or any traditional template.
|
|
38
43
|
|
|
39
44
|
```html
|
|
40
|
-
<script src="https://unpkg.com/@samline/forms@
|
|
45
|
+
<script src="https://unpkg.com/@samline/forms@2.0.0/dist/browser/global.global.js"></script>
|
|
41
46
|
```
|
|
42
47
|
|
|
43
|
-
Pin the version in production.
|
|
48
|
+
> Pin the version in production. Replace `2.0.0` with the version you ship.
|
|
44
49
|
|
|
45
|
-
The browser
|
|
50
|
+
The browser bundle exposes a single global: `window.Forms`.
|
|
46
51
|
|
|
47
52
|
```html
|
|
48
53
|
<form id="contact-form">
|
|
49
54
|
<input name="email" type="email" />
|
|
55
|
+
<button type="submit">Send</button>
|
|
50
56
|
</form>
|
|
51
57
|
|
|
52
|
-
<script src="https://unpkg.com/@samline/forms@
|
|
58
|
+
<script src="https://unpkg.com/@samline/forms@2.0.0/dist/browser/global.global.js"></script>
|
|
53
59
|
<script>
|
|
54
|
-
const contactForm = window.
|
|
60
|
+
const contactForm = window.Forms.newForm({ id: 'contact-form' })
|
|
61
|
+
|
|
62
|
+
contactForm.onSubmit(async (form, data, formData) => {
|
|
63
|
+
await fetch('/api/contact', { method: 'POST', body: formData })
|
|
64
|
+
})
|
|
65
|
+
|
|
55
66
|
contactForm.validate()
|
|
56
67
|
</script>
|
|
57
68
|
```
|
|
58
69
|
|
|
70
|
+
The browser surface keeps a small registry under `Forms.available`, keyed by the `id` you pass to `Forms.newForm`. Each successful `newForm` call stores the returned controller there, and `Forms.destroyForm(id)` calls `destroy()` and removes the entry. Use `Forms.form` directly when you need the factory without the registry side-effect.
|
|
71
|
+
|
|
72
|
+
See [docs/browser.md](docs/browser.md) for the full browser surface.
|
|
73
|
+
|
|
74
|
+
---
|
|
75
|
+
|
|
59
76
|
## Entrypoints
|
|
60
77
|
|
|
61
|
-
| Entrypoint |
|
|
78
|
+
| Entrypoint | When to use |
|
|
62
79
|
| --- | --- |
|
|
63
|
-
| `@samline/forms` | Main vanilla API |
|
|
64
|
-
| `@samline/forms/
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
| `@samline/forms/vue` | Vue composable |
|
|
68
|
-
| `@samline/forms/svelte` | Svelte store and action |
|
|
69
|
-
| `@samline/forms/browser` | Browser global bundle |
|
|
80
|
+
| `@samline/forms` | Main vanilla API for bundlers, ESM, or CJS consumers. |
|
|
81
|
+
| `@samline/forms/browser` | Pre-bundled IIFE that registers `window.Forms` for direct `<script>` usage. |
|
|
82
|
+
|
|
83
|
+
---
|
|
70
84
|
|
|
71
85
|
## Quick Start
|
|
72
86
|
|
|
@@ -82,207 +96,81 @@ const contactForm = form('contact-form', {
|
|
|
82
96
|
}
|
|
83
97
|
})
|
|
84
98
|
|
|
85
|
-
contactForm.
|
|
86
|
-
console.log(
|
|
99
|
+
contactForm.watch('email', value => {
|
|
100
|
+
console.log('email is now:', value)
|
|
87
101
|
})
|
|
88
|
-
```
|
|
89
|
-
|
|
90
|
-
## What You Can Do
|
|
91
|
-
|
|
92
|
-
- bind to a form by id, element, or ref-like target
|
|
93
|
-
- read and write field values
|
|
94
|
-
- serialize form values to both a plain object and `FormData`
|
|
95
|
-
- watch individual fields and subscribe to global form state
|
|
96
|
-
- prefill values from the current URL query string
|
|
97
|
-
- mark filled and error states through DOM attributes for styling
|
|
98
|
-
- run built-in validation rules and custom validators
|
|
99
|
-
- trigger submit handlers with optional auto-submit behavior
|
|
100
|
-
- reset, inspect, and destroy the controller cleanly
|
|
101
|
-
|
|
102
|
-
## API Reference
|
|
103
|
-
|
|
104
|
-
### form(target, options)
|
|
105
|
-
|
|
106
|
-
Creates a controller from:
|
|
107
|
-
|
|
108
|
-
- a form id string
|
|
109
|
-
- a real `HTMLFormElement`
|
|
110
|
-
- a ref-like object with `current`
|
|
111
102
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
- `element`: the bound `HTMLFormElement | null`
|
|
115
|
-
- `f`: alias of `element`
|
|
116
|
-
- `options`: normalized controller options
|
|
117
|
-
|
|
118
|
-
### Submission
|
|
119
|
-
|
|
120
|
-
- `onSubmit(callback, preventDefault?)`
|
|
121
|
-
- `autoSubmit(options?)`
|
|
122
|
-
- `disableAutoSubmit()`
|
|
123
|
-
|
|
124
|
-
`onSubmit` accepts an optional second argument named `preventDefault`.
|
|
125
|
-
|
|
126
|
-
- `onSubmit(callback)` is equivalent to `onSubmit(callback, true)`
|
|
127
|
-
- with `true`, valid submissions are intercepted, which is the right choice for `fetch` or AJAX flows
|
|
128
|
-
- with `false`, valid submissions continue with the browser's native form submit behavior
|
|
129
|
-
- invalid submissions are still prevented, even when you pass `false`
|
|
130
|
-
|
|
131
|
-
### Field observation
|
|
132
|
-
|
|
133
|
-
- `watch(field, callback)`
|
|
134
|
-
- `observe(field, callback)`
|
|
135
|
-
- `subscribe(listener)`
|
|
136
|
-
- `unwatch(field?, callback?)`
|
|
137
|
-
|
|
138
|
-
### Field values
|
|
139
|
-
|
|
140
|
-
- `setValue(name, value)`
|
|
141
|
-
- `getValue(name)`
|
|
142
|
-
- `getField(name)`
|
|
143
|
-
- `prefill(fieldName?)`
|
|
144
|
-
|
|
145
|
-
### Validation and errors
|
|
146
|
-
|
|
147
|
-
- `validate(fields?)`
|
|
148
|
-
- `revalidate(fields?)`
|
|
149
|
-
- `setErrors(fields)`
|
|
150
|
-
- `clearErrors(fields?)`
|
|
151
|
-
|
|
152
|
-
Manual errors created with `setErrors()` are cleared per field by default as soon as that field changes, including updates triggered through `setValue()`. Pass `clearManualErrorsOnChange: false` to keep manual errors until you clear them explicitly.
|
|
153
|
-
|
|
154
|
-
### Form lifecycle and state
|
|
155
|
-
|
|
156
|
-
- `getData()`
|
|
157
|
-
- `getState()`
|
|
158
|
-
- `append(options)`
|
|
159
|
-
- `reset()`
|
|
160
|
-
- `destroy()`
|
|
161
|
-
|
|
162
|
-
### Validation options
|
|
163
|
-
|
|
164
|
-
Built-in rules supported through `validators`:
|
|
165
|
-
|
|
166
|
-
- `required`
|
|
167
|
-
- `minLength`
|
|
168
|
-
- `maxLength`
|
|
169
|
-
- `pattern`
|
|
170
|
-
- `validate` for custom callbacks
|
|
171
|
-
|
|
172
|
-
Controller options also include:
|
|
173
|
-
|
|
174
|
-
- `autoValidate` to validate on initialization and subsequent field changes
|
|
175
|
-
- `clearErrorsOnSubmit` to reset manual errors before submit validation runs
|
|
176
|
-
- `clearManualErrorsOnChange` to clear only the changed field's manual error before the normal validation flow continues
|
|
177
|
-
|
|
178
|
-
```ts
|
|
179
|
-
const profileForm = form('profile-form', {
|
|
180
|
-
clearManualErrorsOnChange: false
|
|
103
|
+
contactForm.onSubmit(async (_element, _data, formData) => {
|
|
104
|
+
await fetch('/api/contact', { method: 'POST', body: formData })
|
|
181
105
|
})
|
|
182
106
|
```
|
|
183
107
|
|
|
184
|
-
|
|
108
|
+
What this does:
|
|
185
109
|
|
|
186
|
-
|
|
110
|
+
- Binds to the form with id `contact-form`.
|
|
111
|
+
- Adds `css-filled` / `css-error` attributes on fields so you can style them with CSS.
|
|
112
|
+
- Validates `email` on every change and on submit.
|
|
113
|
+
- Intercepts valid submits (the default for `onSubmit`) and hands a real `FormData` instance to your handler.
|
|
187
114
|
|
|
188
|
-
|
|
189
|
-
import { form } from '@samline/forms'
|
|
190
|
-
|
|
191
|
-
const profileForm = form('profile-form')
|
|
192
|
-
```
|
|
193
|
-
|
|
194
|
-
### Bind by element
|
|
195
|
-
|
|
196
|
-
```ts
|
|
197
|
-
const element = document.querySelector('#profile-form') as HTMLFormElement
|
|
198
|
-
const profileForm = form(element)
|
|
199
|
-
```
|
|
200
|
-
|
|
201
|
-
### Watch a field
|
|
202
|
-
|
|
203
|
-
```ts
|
|
204
|
-
const profileForm = form('profile-form')
|
|
205
|
-
|
|
206
|
-
profileForm.watch('email', value => {
|
|
207
|
-
console.log('email changed:', value)
|
|
208
|
-
})
|
|
209
|
-
```
|
|
210
|
-
|
|
211
|
-
### Validate with built-in rules
|
|
212
|
-
|
|
213
|
-
```ts
|
|
214
|
-
const profileForm = form('profile-form', {
|
|
215
|
-
validators: {
|
|
216
|
-
email: {
|
|
217
|
-
required: true,
|
|
218
|
-
pattern: /^[^\s@]+@[^\s@]+\.[^\s@]+$/
|
|
219
|
-
},
|
|
220
|
-
password: {
|
|
221
|
-
minLength: 8
|
|
222
|
-
}
|
|
223
|
-
}
|
|
224
|
-
})
|
|
115
|
+
---
|
|
225
116
|
|
|
226
|
-
|
|
227
|
-
console.log(result.isValid, result.errors)
|
|
228
|
-
```
|
|
117
|
+
## What You Can Build
|
|
229
118
|
|
|
230
|
-
|
|
119
|
+
- Contact, newsletter, login, signup, checkout, and profile forms.
|
|
120
|
+
- Forms that submit with `fetch` while keeping native `FormData` payloads.
|
|
121
|
+
- Autosave / autosubmit flows with optional debounce.
|
|
122
|
+
- Visual feedback driven by `css-filled` and `css-error` attributes.
|
|
123
|
+
- Progressive enhancement on top of any existing HTML form.
|
|
124
|
+
- Forms rendered server-side (Blade, Twig, ERB) that still want client-side validation.
|
|
231
125
|
|
|
232
|
-
|
|
233
|
-
const profileForm = form('profile-form')
|
|
126
|
+
---
|
|
234
127
|
|
|
235
|
-
|
|
236
|
-
await fetch('/api/profile', {
|
|
237
|
-
method: 'POST',
|
|
238
|
-
body: formData
|
|
239
|
-
})
|
|
240
|
-
})
|
|
241
|
-
```
|
|
128
|
+
## API at a Glance
|
|
242
129
|
|
|
243
|
-
|
|
130
|
+
The controller is built around one factory and a small set of focused methods. Most methods are chainable.
|
|
244
131
|
|
|
245
|
-
|
|
132
|
+
| Group | Methods |
|
|
133
|
+
| --- | --- |
|
|
134
|
+
| Lifecycle | [`form`](docs/api/form.md) · [`destroy`](docs/api/destroy.md) · [`reset`](docs/api/reset.md) |
|
|
135
|
+
| Properties | [`element`](docs/api/element.md) · [`options`](docs/options.md) |
|
|
136
|
+
| Submission | [`onSubmit`](docs/api/on-submit.md) · [`autoSubmit`](docs/api/auto-submit.md) · [`disableAutoSubmit`](docs/api/disable-auto-submit.md) |
|
|
137
|
+
| Field observation | [`watch`](docs/api/watch.md) · [`observe`](docs/api/observe.md) · [`unwatch`](docs/api/unwatch.md) · [`subscribe`](docs/api/subscribe.md) |
|
|
138
|
+
| Field values | [`setValue`](docs/api/set-value.md) · [`getValue`](docs/api/get-value.md) · [`getField`](docs/api/get-field.md) · [`prefill`](docs/api/prefill.md) · [`format`](docs/api/format.md) · [`formatAll`](docs/api/format.md) |
|
|
139
|
+
| Validation | [`validate`](docs/api/validate.md) · [`revalidate`](docs/api/revalidate.md) · [`setErrors`](docs/api/set-errors.md) · [`clearErrors`](docs/api/clear-errors.md) |
|
|
140
|
+
| State and data | [`getData`](docs/api/get-data.md) · [`getState`](docs/api/get-state.md) · [`append`](docs/api/append.md) |
|
|
141
|
+
| Pure helpers | [`parseFormData`](docs/api/parse-form-data.md) · [`validateValues`](docs/api/validate-values.md) · [`validateFieldValue`](docs/api/validate-field-value.md) |
|
|
246
142
|
|
|
247
|
-
|
|
248
|
-
const profileForm = form('profile-form')
|
|
143
|
+
See the full per-method reference in [`docs/api/`](docs/api/index.md).
|
|
249
144
|
|
|
250
|
-
|
|
251
|
-
console.log('validation passed')
|
|
252
|
-
}, false)
|
|
253
|
-
```
|
|
145
|
+
---
|
|
254
146
|
|
|
255
|
-
|
|
147
|
+
## Optional peer: `@samline/formatter`
|
|
256
148
|
|
|
257
|
-
|
|
149
|
+
[`format`](docs/api/format.md) and [`formatAll`](docs/api/format.md) rely on the optional peer dependency [`@samline/formatter`](https://github.com/samline/formatter). Install it when you need input masks (phone, credit-card, date, time, numeral, general):
|
|
258
150
|
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
```ts
|
|
262
|
-
const profileForm = form('profile-form')
|
|
263
|
-
profileForm.prefill()
|
|
151
|
+
```bash
|
|
152
|
+
npm install @samline/formatter
|
|
264
153
|
```
|
|
265
154
|
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
```ts
|
|
269
|
-
const profileForm = form('profile-form')
|
|
270
|
-
|
|
271
|
-
const unsubscribe = profileForm.subscribe(state => {
|
|
272
|
-
console.log(state.values)
|
|
273
|
-
console.log(state.errors)
|
|
274
|
-
})
|
|
155
|
+
If the peer is not installed, the methods log a single `console.error` explaining how to install it and return the controller unchanged — the rest of the form keeps working. See [docs/recipes.md → 13. Format inputs with `@samline/formatter`](docs/recipes.md#13-format-inputs-with-samlineformatter) for end-to-end examples.
|
|
275
156
|
|
|
276
|
-
|
|
277
|
-
```
|
|
157
|
+
---
|
|
278
158
|
|
|
279
159
|
## Documentation
|
|
280
160
|
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
161
|
+
Full API reference, guides, and examples are available at **[samline.github.io/forms](https://samline.github.io/forms)**.
|
|
162
|
+
|
|
163
|
+
| Doc | Purpose |
|
|
164
|
+
| --- | --- |
|
|
165
|
+
| [docs/getting-started.md](docs/getting-started.md) | Concepts, observable contract, lifecycle, and side-effect overview. |
|
|
166
|
+
| [docs/options.md](docs/options.md) | Full `FormControllerOptions` reference. |
|
|
167
|
+
| [docs/css-styling.md](docs/css-styling.md) | `css-filled` and `css-error` styling recipes. |
|
|
168
|
+
| [docs/typescript.md](docs/typescript.md) | Every exported TypeScript type, with examples. |
|
|
169
|
+
| [docs/api/index.md](docs/api/index.md) | One page per public method. |
|
|
170
|
+
| [docs/recipes.md](docs/recipes.md) | End-to-end patterns: fetch submit, server errors, autosave, multi-step, etc. |
|
|
171
|
+
| [docs/browser.md](docs/browser.md) | Browser global (`window.Forms`) usage. |
|
|
172
|
+
|
|
173
|
+
---
|
|
286
174
|
|
|
287
175
|
## License
|
|
288
176
|
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
type FormatType = 'general' | 'phone' | 'numeral' | 'date' | 'time' | 'creditCard' | 'creditCardType';
|
|
2
|
+
interface FieldFormatConfig {
|
|
3
|
+
/** One of the supported `@samline/formatter` `FormatType` values. */
|
|
4
|
+
type: FormatType;
|
|
5
|
+
/** Name of the visible input(s) inside the bound form. */
|
|
6
|
+
field: string | string[];
|
|
7
|
+
/**
|
|
8
|
+
* Optional name for the hidden raw mirror. Defaults to
|
|
9
|
+
* `${fieldName}Raw`. The mirror is created on first `format()`
|
|
10
|
+
* call and reused on subsequent calls.
|
|
11
|
+
*/
|
|
12
|
+
rawField?: string;
|
|
13
|
+
/**
|
|
14
|
+
* Format-specific options forwarded to `@samline/formatter`.
|
|
15
|
+
* Any key documented for the chosen `type` is accepted (e.g.
|
|
16
|
+
* `country`, `delimiter`, `numeralDecimalMark`, `datePattern`).
|
|
17
|
+
*/
|
|
18
|
+
options?: Record<string, unknown>;
|
|
19
|
+
}
|
|
20
|
+
type FieldFormatConfigMap = Record<string, FieldFormatConfig>;
|
|
21
|
+
type FormDataPrimitive = FormDataEntryValue;
|
|
22
|
+
type SerializedFormValue = FormDataPrimitive | FormDataPrimitive[];
|
|
23
|
+
type FormFieldValue = string | string[] | File[] | undefined;
|
|
24
|
+
type FormValues = Record<string, FormFieldValue>;
|
|
25
|
+
type FormErrors = Record<string, string[]>;
|
|
26
|
+
type RuleConfig<T> = T | {
|
|
27
|
+
value: T;
|
|
28
|
+
message?: string;
|
|
29
|
+
};
|
|
30
|
+
interface FieldValidationContext {
|
|
31
|
+
field: string;
|
|
32
|
+
value: FormFieldValue;
|
|
33
|
+
values: FormValues;
|
|
34
|
+
}
|
|
35
|
+
type FieldValidator = (context: FieldValidationContext) => string | undefined | null | false | true;
|
|
36
|
+
interface FieldValidationRules {
|
|
37
|
+
required?: RuleConfig<boolean>;
|
|
38
|
+
minLength?: RuleConfig<number>;
|
|
39
|
+
maxLength?: RuleConfig<number>;
|
|
40
|
+
pattern?: RuleConfig<RegExp>;
|
|
41
|
+
validate?: FieldValidator | FieldValidator[];
|
|
42
|
+
}
|
|
43
|
+
type ValidationSchema = Record<string, FieldValidationRules>;
|
|
44
|
+
interface VisualAttributes {
|
|
45
|
+
filled: string;
|
|
46
|
+
error: string;
|
|
47
|
+
}
|
|
48
|
+
interface AutoSubmitOptions {
|
|
49
|
+
debounce?: number;
|
|
50
|
+
}
|
|
51
|
+
interface FormControllerOptions {
|
|
52
|
+
attributes?: Partial<VisualAttributes>;
|
|
53
|
+
autoValidate?: boolean;
|
|
54
|
+
autoSubmit?: boolean | AutoSubmitOptions;
|
|
55
|
+
clearManualErrorsOnChange?: boolean;
|
|
56
|
+
clearErrorsOnSubmit?: boolean;
|
|
57
|
+
validators?: ValidationSchema;
|
|
58
|
+
/**
|
|
59
|
+
* Declarative format configuration. Each entry is applied during
|
|
60
|
+
* `form()` initialization using the same logic as `controller.format(...)`.
|
|
61
|
+
* The key is just an identifier — the visible field name lives in
|
|
62
|
+
* `FieldFormatConfig.field`.
|
|
63
|
+
*/
|
|
64
|
+
formats?: FieldFormatConfigMap;
|
|
65
|
+
}
|
|
66
|
+
interface SerializedFormResult {
|
|
67
|
+
data: Record<string, SerializedFormValue>;
|
|
68
|
+
formData: FormData;
|
|
69
|
+
}
|
|
70
|
+
interface ValidationResult {
|
|
71
|
+
isValid: boolean;
|
|
72
|
+
errors: FormErrors;
|
|
73
|
+
}
|
|
74
|
+
interface FormStateSnapshot {
|
|
75
|
+
values: FormValues;
|
|
76
|
+
errors: FormErrors;
|
|
77
|
+
filledFields: string[];
|
|
78
|
+
isValid: boolean;
|
|
79
|
+
isValidated: boolean;
|
|
80
|
+
autoSubmit: boolean;
|
|
81
|
+
submitCount: number;
|
|
82
|
+
}
|
|
83
|
+
type FormStateListener = (state: FormStateSnapshot) => void;
|
|
84
|
+
type FormFieldElement = HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement;
|
|
85
|
+
type FormTarget = string | HTMLFormElement | {
|
|
86
|
+
current: HTMLFormElement | null;
|
|
87
|
+
} | null | undefined;
|
|
88
|
+
interface AppendContentOptions {
|
|
89
|
+
tag: keyof HTMLElementTagNameMap;
|
|
90
|
+
content: string;
|
|
91
|
+
class?: string;
|
|
92
|
+
atStart?: boolean;
|
|
93
|
+
}
|
|
94
|
+
type FormFieldWatcher = (value: FormFieldValue, field: FormFieldElement | FormFieldElement[] | null, form: HTMLFormElement, state: FormStateSnapshot) => void;
|
|
95
|
+
type FormSubmitHandler = (form: HTMLFormElement, data: Record<string, SerializedFormValue>, formData: FormData, state: FormStateSnapshot) => void;
|
|
96
|
+
interface FormController {
|
|
97
|
+
readonly element: HTMLFormElement | null;
|
|
98
|
+
readonly f: HTMLFormElement | null;
|
|
99
|
+
readonly options: FormControllerOptions;
|
|
100
|
+
onSubmit: (callback: FormSubmitHandler, preventDefault?: boolean) => FormController;
|
|
101
|
+
watch: (field: string, callback: FormFieldWatcher) => FormController;
|
|
102
|
+
observe: (field: string, callback: FormFieldWatcher) => () => void;
|
|
103
|
+
unwatch: (field?: string, callback?: FormFieldWatcher) => FormController;
|
|
104
|
+
subscribe: (listener: FormStateListener) => () => void;
|
|
105
|
+
prefill: (fieldName?: string) => FormController;
|
|
106
|
+
append: (options: AppendContentOptions) => HTMLElement | null;
|
|
107
|
+
setErrors: (fields: string[] | FormErrors) => FormController;
|
|
108
|
+
clearErrors: (fields?: string[]) => FormController;
|
|
109
|
+
setValue: (name: string, value: unknown) => FormController;
|
|
110
|
+
validate: (fields?: string[]) => ValidationResult;
|
|
111
|
+
revalidate: (fields?: string[]) => ValidationResult;
|
|
112
|
+
/**
|
|
113
|
+
* Apply the same `@samline/formatter` pipeline to a single field or
|
|
114
|
+
* to a list of fields sharing one configuration. Chainable.
|
|
115
|
+
*
|
|
116
|
+
* Requires the optional peer dependency `@samline/formatter`. When
|
|
117
|
+
* the peer is not installed the call logs a single `console.error`
|
|
118
|
+
* and returns the controller unchanged.
|
|
119
|
+
*/
|
|
120
|
+
format: (config: FieldFormatConfig) => FormController;
|
|
121
|
+
formatAll: (config: FieldFormatConfig) => FormController;
|
|
122
|
+
reset: () => FormController;
|
|
123
|
+
autoSubmit: (options?: boolean | AutoSubmitOptions) => FormController;
|
|
124
|
+
disableAutoSubmit: () => FormController;
|
|
125
|
+
getValue: (name: string) => FormFieldValue;
|
|
126
|
+
getField: (name: string) => FormFieldElement | FormFieldElement[] | null;
|
|
127
|
+
getData: () => SerializedFormResult;
|
|
128
|
+
getState: () => FormStateSnapshot;
|
|
129
|
+
destroy: () => void;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
interface NewFormInput {
|
|
133
|
+
id: string;
|
|
134
|
+
options?: FormControllerOptions;
|
|
135
|
+
}
|
|
136
|
+
interface FormsAvailable {
|
|
137
|
+
[id: string]: FormController;
|
|
138
|
+
}
|
|
139
|
+
interface FormsApi {
|
|
140
|
+
form: (target: FormTarget, options?: FormControllerOptions) => FormController;
|
|
141
|
+
newForm: (input: NewFormInput) => FormController | undefined;
|
|
142
|
+
destroyForm: (id: string) => void;
|
|
143
|
+
available: FormsAvailable;
|
|
144
|
+
}
|
|
145
|
+
declare const Forms: FormsApi;
|
|
146
|
+
declare global {
|
|
147
|
+
interface Window {
|
|
148
|
+
Forms: FormsApi;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
export { type FormsApi, type FormsAvailable, type NewFormInput, Forms as default };
|