@embeddables/forms 0.2.0 → 0.3.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 CHANGED
@@ -27,7 +27,7 @@ Example:
27
27
  ```tsx
28
28
  import { EmbeddablesProvider } from '@embeddables/core/react'
29
29
  import { analytics } from '@embeddables/analytics/react'
30
- import { forms, useForm, useFormField } from '@embeddables/forms/react'
30
+ import { forms, useForm, useFormField, useFormFileUpload } from '@embeddables/forms/react'
31
31
  import { config } from './embeddables/_dist/config.ts'
32
32
 
33
33
  function SignupField() {
@@ -85,6 +85,35 @@ export function App() {
85
85
  - One live `FormInstance` per `schema.id` is shared across hooks in the same client.
86
86
  - Imperative consumers can also call `form.subscribe(listener)` to observe value and error changes.
87
87
 
88
+ ### File inputs
89
+
90
+ For `value_type: file` fields, use `useFormFileUpload`. It uploads through the publishable-key API, commits the returned `FormFileRef`, and exposes `inputProps` for a native `<input type="file">`.
91
+
92
+ Example:
93
+
94
+ ```tsx
95
+ function IdPhotoField() {
96
+ const { form } = useForm({ formId: 'intake' })
97
+ const { value, error, isLoading, uploadError, inputProps } = useFormFileUpload({
98
+ form,
99
+ key: 'id_photo',
100
+ })
101
+
102
+ return (
103
+ <label>
104
+ ID photo
105
+ <input {...inputProps} accept="image/jpeg,image/png" />
106
+ {isLoading && <span>Uploading…</span>}
107
+ {uploadError && <span>{uploadError}</span>}
108
+ {error?.[0]}
109
+ {value?.name && <span>{value.name}</span>}
110
+ </label>
111
+ )
112
+ }
113
+ ```
114
+
115
+ Imperative equivalent: `const ref = await form.uploadFile({ key: 'id_photo', file })` then `await form.set({ id_photo: ref })`.
116
+
88
117
  ## Quick start
89
118
 
90
119
  ```typescript
@@ -98,7 +127,8 @@ const schema = {
98
127
  {
99
128
  key: 'email',
100
129
  label: 'Email',
101
- type: 'email',
130
+ value_type: 'email',
131
+ field_type: 'user_input',
102
132
  validations: { required: true },
103
133
  },
104
134
  ],
@@ -126,15 +156,32 @@ from `embeddables/_dist` are already typed.
126
156
 
127
157
  ## Schema
128
158
 
129
- Each form is one object: `id`, optional `name`, and `fields`. Supported field
130
- types: `text`, `email`, `number`, `boolean`, `select`, `multiselect`, `json`.
159
+ Each form is one object: `id`, optional `name`, and `fields`. Each field has a
160
+ `value_type` (`text`, `email`, `number`, `boolean`, `select`, `multiselect`,
161
+ `json`, `file`).
162
+
163
+ Use `getFieldByKey(fieldKey)` for presentation metadata (`display_text`,
164
+ `display_description`, option `display_text`, `field_type`) instead of
165
+ duplicating copy in markup. `local_only` values still persist but are omitted
166
+ from `data:updated` / `field:updated` analytics patches.
167
+
168
+ ```ts
169
+ const plan = form.getFieldByKey('plan')
170
+ plan?.display_text
171
+ plan?.options?.[0]?.display_text
172
+ ```
131
173
 
132
174
  Declarative rules: `required`, `minLength`, `maxLength`, `min`, `max`,
133
- `pattern`, `oneOf`, and optional synchronous `validations.custom`.
175
+ `pattern`, `oneOf`, and optional synchronous `validations.custom`. File fields
176
+ also accept `validations.accept` (MIME list) and `validations.maxSize` (bytes).
177
+ Compatible field types may declare `protocolFieldId` to link a form field to a
178
+ protocol question. `json` fields cannot. `FormFileRef` is exported for typed
179
+ file values.
134
180
 
135
181
  A `select` or `multiselect` field declares its choices in a field-level `options`
136
- array of `{ value, label?, exclusive? }`, and the field's `type` — not the name of
137
- a rule — decides whether one or many of them may be selected. An option marked
182
+ array of `{ key, display_text, display_description?, exclusive? }`. The field's
183
+ `value_type` — not the name of a rule — decides whether one or many of them may
184
+ be selected. An option marked
138
185
  `exclusive` on a `multiselect` cannot coexist with any other value: selecting it
139
186
  clears the rest, and selecting a regular option afterwards clears it. Those two
140
187
  types no longer accept `validations.oneOf`, which stays available on every other
@@ -156,9 +203,11 @@ on `initForms` / `forms()`.
156
203
  | ------ | ------- |
157
204
  | `set({ … })` | Validate and persist a patch atomically; optional analytics; best-effort durable R2 write when configured |
158
205
  | `get(key)` / `getAll()` | Read declared fields from storage |
206
+ | `getFieldByKey(key)` | Read resolved schema metadata for one declared field |
159
207
  | `getValueByProtocolFieldId(protocolFieldId)` | Read by schema `protocolFieldId` (typed like `get()` for the backing field; `undefined` if unknown or unset) |
160
208
  | `validate({ … })` | Check values without writing or tracking |
161
209
  | `submit()` | Validate all fields, best-effort durable R2 write, and emit `form:submitted` when analytics is configured |
210
+ | `uploadFile({ key, file, fileName? })` | Upload bytes for a `value_type: file` field; returns `FormFileRef` (caller commits with `.set()`) |
162
211
  | `errors()` | Current validation messages |
163
212
  | `clear()` | Remove this form's stored values |
164
213