effect-atom-svelte 0.2.0 → 0.4.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 +5 -2
- package/dist/Form.d.ts +605 -0
- package/dist/Form.js +67 -0
- package/dist/Hooks.svelte.d.ts +70 -10
- package/dist/Hooks.svelte.js +489 -163
- package/dist/HydrationBoundary.svelte +134 -17
- package/dist/HydrationBoundary.svelte.d.ts +13 -4
- package/dist/Inspector.d.ts +7 -3
- package/dist/Inspector.js +113 -29
- package/dist/RegistryContext.d.ts +2 -0
- package/dist/RegistryContext.js +13 -5
- package/dist/ScopedAtom.d.ts +5 -4
- package/dist/ScopedAtom.js +20 -11
- package/dist/SvelteKit.d.ts +71 -12
- package/dist/SvelteKit.js +133 -54
- package/dist/index.d.ts +10 -1
- package/dist/index.js +10 -1
- package/dist/internal/awaitWrite.d.ts +11 -0
- package/dist/internal/awaitWrite.js +27 -0
- package/dist/internal/boundaries.d.ts +9 -0
- package/dist/internal/boundaries.js +10 -0
- package/dist/internal/disposal.d.ts +13 -0
- package/dist/internal/disposal.js +43 -0
- package/dist/internal/form/array.d.ts +21 -0
- package/dist/internal/form/array.js +57 -0
- package/dist/internal/form/constraints.d.ts +13 -0
- package/dist/internal/form/constraints.js +166 -0
- package/dist/internal/form/engine.d.ts +56 -0
- package/dist/internal/form/engine.js +842 -0
- package/dist/internal/form/engineOf.d.ts +26 -0
- package/dist/internal/form/engineOf.js +43 -0
- package/dist/internal/form/field.d.ts +30 -0
- package/dist/internal/form/field.js +68 -0
- package/dist/internal/form/group.d.ts +13 -0
- package/dist/internal/form/group.js +20 -0
- package/dist/internal/form/instance.d.ts +41 -0
- package/dist/internal/form/instance.js +135 -0
- package/dist/internal/form/issues.d.ts +23 -0
- package/dist/internal/form/issues.js +144 -0
- package/dist/internal/form/paths.d.ts +47 -0
- package/dist/internal/form/paths.js +175 -0
- package/dist/internal/form/types.d.ts +80 -0
- package/dist/internal/form/types.js +1 -0
- package/dist/internal/form/validationError.d.ts +13 -0
- package/dist/internal/form/validationError.js +6 -0
- package/dist/internal/hydration.d.ts +11 -2
- package/dist/internal/hydration.js +26 -4
- package/dist/internal/nodeInternals.d.ts +6 -0
- package/dist/internal/nodeInternals.js +64 -7
- package/dist/internal/renderEnd.d.ts +13 -0
- package/dist/internal/renderEnd.js +34 -0
- package/dist/internal/teardown.svelte.d.ts +17 -0
- package/dist/internal/teardown.svelte.js +44 -0
- package/dist/internal/troubleshooting.d.ts +2 -0
- package/dist/internal/troubleshooting.js +3 -0
- package/package.json +12 -10
package/dist/Form.js
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Forms driven by one Effect Schema. `Form.make` defines a form from a Schema whose encoded side is
|
|
3
|
+
* what inputs hold and whose decoded side is what a submit sends; `useForm` creates an instance in
|
|
4
|
+
* a component, and `Form.instance` creates one without Svelte. The module is headless: it gives
|
|
5
|
+
* values, errors and element attributes, and no components.
|
|
6
|
+
*
|
|
7
|
+
* @since 0.4.0
|
|
8
|
+
*/
|
|
9
|
+
import { Duration } from "effect";
|
|
10
|
+
import { AtomRegistry } from "effect/reactivity";
|
|
11
|
+
import { engineOf, initialGetter } from "./internal/form/engineOf.js";
|
|
12
|
+
import { FormValidationError } from "./internal/form/validationError.js";
|
|
13
|
+
// A const and a type of the same name, as in Effect's modules.
|
|
14
|
+
/* oxlint-disable eslint/no-redeclare */
|
|
15
|
+
/**
|
|
16
|
+
* Marks a value as a form definition: every definition has this key, set to this same string.
|
|
17
|
+
*
|
|
18
|
+
* @stability unstable
|
|
19
|
+
* @since 0.4.0
|
|
20
|
+
* @category type ids
|
|
21
|
+
*/
|
|
22
|
+
export const TypeId = "~effect-atom-svelte/Form";
|
|
23
|
+
const definitionOf = (schema, options) => ({
|
|
24
|
+
checkHook: options.checkHook,
|
|
25
|
+
debounce: options.debounce === undefined ? 0 : Duration.toMillis(options.debounce),
|
|
26
|
+
leafHook: options.leafHook,
|
|
27
|
+
name: options.name,
|
|
28
|
+
revalidateOn: options.revalidateOn ?? "input",
|
|
29
|
+
runtime: options.runtime,
|
|
30
|
+
schema,
|
|
31
|
+
validateOn: options.validateOn ?? "blur",
|
|
32
|
+
});
|
|
33
|
+
export function make(schema, options = {}) {
|
|
34
|
+
return {
|
|
35
|
+
[TypeId]: TypeId,
|
|
36
|
+
definition: definitionOf(schema, options),
|
|
37
|
+
schema,
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Whether a value is a form definition from `Form.make`.
|
|
42
|
+
*
|
|
43
|
+
* @stability unstable
|
|
44
|
+
* @since 0.4.0
|
|
45
|
+
* @category guards
|
|
46
|
+
*/
|
|
47
|
+
export const isForm = (u) => typeof u === "object" && u !== null && TypeId in u;
|
|
48
|
+
/* oxlint-disable eslint/no-redeclare */
|
|
49
|
+
/**
|
|
50
|
+
* Why `submit` didn't call the submit atom: the values didn't decode. `errors` lists what showed.
|
|
51
|
+
* Its `_tag` is `"FormValidationError"`, for `Effect.catchTag`.
|
|
52
|
+
*
|
|
53
|
+
* @stability unstable
|
|
54
|
+
* @since 0.4.0
|
|
55
|
+
* @category errors
|
|
56
|
+
*/
|
|
57
|
+
export const ValidationError = FormValidationError;
|
|
58
|
+
export function instance(form, options) {
|
|
59
|
+
const initial = options.initialValues;
|
|
60
|
+
const getInitial = initialGetter(initial);
|
|
61
|
+
return engineOf(form, {
|
|
62
|
+
...options,
|
|
63
|
+
initialValues: getInitial ? getInitial() : initial,
|
|
64
|
+
registry: options.registry ?? AtomRegistry.make(),
|
|
65
|
+
validates: true,
|
|
66
|
+
}).root;
|
|
67
|
+
}
|
package/dist/Hooks.svelte.d.ts
CHANGED
|
@@ -4,8 +4,10 @@
|
|
|
4
4
|
* @since 0.1.0
|
|
5
5
|
*/
|
|
6
6
|
import { Exit } from "effect";
|
|
7
|
+
import type { Schema } from "effect";
|
|
7
8
|
import { AsyncResult, Atom } from "effect/reactivity";
|
|
8
9
|
import type { AtomRef } from "effect/reactivity";
|
|
10
|
+
import type * as Form from "./Form.ts";
|
|
9
11
|
/**
|
|
10
12
|
* An atom, or a getter so the hook follows the atom when reactive state picks a different one.
|
|
11
13
|
*
|
|
@@ -122,12 +124,14 @@ export declare const useAtomMount: (input: AtomInput<Atom.Atom<unknown>>) => voi
|
|
|
122
124
|
*
|
|
123
125
|
* In `promise` and `promiseExit` modes the setter waits for the atom's next settled result. The
|
|
124
126
|
* wait holds the atom, so a call still in flight keeps it running after the component is
|
|
125
|
-
* destroyed, until it settles
|
|
127
|
+
* destroyed, until it settles, unless the `RegistryProvider` that created the registry is
|
|
128
|
+
* destroyed: that interrupts the call and settles it as interrupted. The call's `signal` cancels only the wait: an `Atom.fn` already
|
|
126
129
|
* running keeps going (send `Atom.Interrupt` to stop it). A signal that is already aborted settles
|
|
127
130
|
* the call as interrupted without writing, as `fetch` does. Calls on one `Atom.fn` share its single
|
|
128
131
|
* result, so a new call supersedes one in flight and every pending call resolves with the latest
|
|
129
132
|
* call's result. `Atom.Reset` has no result to wait for, so these modes leave it out of their
|
|
130
|
-
* types and reject it; reset with a `value` mode setter.
|
|
133
|
+
* types and reject it; reset with a `value` mode setter. A reset written while a promise-mode call
|
|
134
|
+
* is pending settles that promise as interrupted. To cancel a call, send `Atom.Interrupt`.
|
|
131
135
|
*
|
|
132
136
|
* **Example** (Updating an atom from its current value)
|
|
133
137
|
*
|
|
@@ -207,9 +211,10 @@ export declare const useAtomSubscribe: <A>(input: AtomInput<Atom.Atom<A>>, f: (v
|
|
|
207
211
|
* an atom that can only compute in the browser can start from a value on the server.
|
|
208
212
|
*
|
|
209
213
|
* A value applies once while the atom is held, so a component mounted later, or a second
|
|
210
|
-
* component with its own value, does not overwrite a value the atom has moved on from
|
|
211
|
-
*
|
|
212
|
-
*
|
|
214
|
+
* component with its own value, does not overwrite a value the atom has moved on from since the
|
|
215
|
+
* first applied one. An atom that is already in use without one, read by another component, is set
|
|
216
|
+
* to the value, as `@effect/atom-react` does. Once nothing holds the atom and the registry disposes
|
|
217
|
+
* it, the next component to start it applies its value again. On the server a value lasts one render, so each request against a shared registry applies
|
|
213
218
|
* its own; requests rendering at the same time share the registry's atoms, and with them the value
|
|
214
219
|
* the first one applied.
|
|
215
220
|
*
|
|
@@ -232,8 +237,9 @@ export declare const useAtomSubscribe: <A>(input: AtomInput<Atom.Atom<A>>, f: (v
|
|
|
232
237
|
*/
|
|
233
238
|
export declare const useAtomInitialValues: (initialValues: Iterable<readonly [Atom.Atom<unknown>, unknown]>) => void;
|
|
234
239
|
/**
|
|
235
|
-
* Reads an `AtomRef`, following it when the getter returns a different ref. For
|
|
236
|
-
* ref,
|
|
240
|
+
* Reads an `AtomRef`, following it when the getter returns a different ref. For a writable ref,
|
|
241
|
+
* `current` can be assigned too, which sets the ref, so `bind:value={name.current}` works; a
|
|
242
|
+
* read-only ref, as from `map`, can't be. For one property of a ref, use `useAtomRefPropValue`.
|
|
237
243
|
*
|
|
238
244
|
* A ref has no registry. On the server, one created at module level is shared by every request,
|
|
239
245
|
* so don't write a visitor's data to it there: see
|
|
@@ -253,7 +259,8 @@ export declare const useAtomInitialValues: (initialValues: Iterable<readonly [At
|
|
|
253
259
|
* @since 0.1.0
|
|
254
260
|
* @category hooks
|
|
255
261
|
*/
|
|
256
|
-
export declare
|
|
262
|
+
export declare function useAtomRef<A>(input: AtomInput<AtomRef.AtomRef<A>>): AtomState<A>;
|
|
263
|
+
export declare function useAtomRef<A>(input: AtomInput<AtomRef.ReadonlyRef<A>>): AtomValue<A>;
|
|
257
264
|
/**
|
|
258
265
|
* Returns `ref.prop(prop)`, the `AtomRef` for one property of an `AtomRef`. It takes the ref
|
|
259
266
|
* itself, not a getter, and does not follow later changes to `ref` or `prop`; to read a property
|
|
@@ -276,7 +283,8 @@ export declare const useAtomRef: <A>(input: AtomInput<AtomRef.ReadonlyRef<A>>) =
|
|
|
276
283
|
export declare const useAtomRefProp: <A, K extends keyof A>(ref: AtomRef.AtomRef<A>, prop: K) => AtomRef.AtomRef<A[K]>;
|
|
277
284
|
/**
|
|
278
285
|
* Reads one property of an `AtomRef`, following the getter to a different ref. `prop` is taken
|
|
279
|
-
* once, when the component starts, and is not followed.
|
|
286
|
+
* once, when the component starts, and is not followed. Assigning `current` sets the property, so
|
|
287
|
+
* `bind:checked={done.current}` works.
|
|
280
288
|
*
|
|
281
289
|
* **Example** (Reading one property of a ref)
|
|
282
290
|
*
|
|
@@ -292,7 +300,7 @@ export declare const useAtomRefProp: <A, K extends keyof A>(ref: AtomRef.AtomRef
|
|
|
292
300
|
* @since 0.1.0
|
|
293
301
|
* @category hooks
|
|
294
302
|
*/
|
|
295
|
-
export declare const useAtomRefPropValue: <A, K extends keyof A>(input: AtomInput<AtomRef.AtomRef<A>>, prop: K) =>
|
|
303
|
+
export declare const useAtomRefPropValue: <A, K extends keyof A>(input: AtomInput<AtomRef.AtomRef<A>>, prop: K) => AtomState<A[K]>;
|
|
296
304
|
/**
|
|
297
305
|
* Options for `useAtomResult`.
|
|
298
306
|
*
|
|
@@ -394,3 +402,55 @@ export declare function useAtomSuspense<A, E>(input: AtomInput<Atom.Atom<AsyncRe
|
|
|
394
402
|
export declare function useAtomSuspense<A, E>(input: AtomInput<Atom.Atom<AsyncResult.AsyncResult<A, E>>>, options?: SuspenseOptions & {
|
|
395
403
|
readonly includeFailure?: boolean | undefined;
|
|
396
404
|
}): AtomValue<Promise<A | AsyncResult.Success<A, E> | AsyncResult.Failure<A, E>>>;
|
|
405
|
+
/**
|
|
406
|
+
* Creates an instance of a form from `Form.make` in a component. Its state lives in the
|
|
407
|
+
* component's registry and is released when the component is destroyed; a submit in flight keeps
|
|
408
|
+
* it, and keeps its atom running, until the submit settles, so a save finishes after a dialog
|
|
409
|
+
* closes. Share an instance by passing it down, or create it in the parent whose lifetime it
|
|
410
|
+
* should have, such as a wizard around its steps.
|
|
411
|
+
*
|
|
412
|
+
* Pass `id` from `$props.id()`: field ids are built from it, and must match between the server and
|
|
413
|
+
* the browser. On the server the form renders from `initialValues` and doesn't validate, so there
|
|
414
|
+
* is nothing to hydrate. A getter for `initialValues` is followed: when it changes, a clean form
|
|
415
|
+
* takes the new values and a dirty one keeps its edits.
|
|
416
|
+
*
|
|
417
|
+
* **Example** (A login form)
|
|
418
|
+
*
|
|
419
|
+
* ```svelte
|
|
420
|
+
* <script lang="ts">
|
|
421
|
+
* import { useForm } from "effect-atom-svelte";
|
|
422
|
+
* import { Login, loginAtom } from "./forms.ts";
|
|
423
|
+
*
|
|
424
|
+
* const id = $props.id();
|
|
425
|
+
* const form = useForm(Login, {
|
|
426
|
+
* id,
|
|
427
|
+
* initialValues: { email: "", password: "" },
|
|
428
|
+
* submit: { atom: loginAtom },
|
|
429
|
+
* mapError: (error) =>
|
|
430
|
+
* error._tag === "WrongPassword"
|
|
431
|
+
* ? { form: "Wrong email or password" }
|
|
432
|
+
* : undefined,
|
|
433
|
+
* });
|
|
434
|
+
* const email = form.field("email");
|
|
435
|
+
* </script>
|
|
436
|
+
*
|
|
437
|
+
* <form {...form.attrs}>
|
|
438
|
+
* <label for={email.id}>Email</label>
|
|
439
|
+
* <input {...email.attrs} bind:value={email.value} />
|
|
440
|
+
* {#if email.error}
|
|
441
|
+
* <p id={email.errorId}>{email.error}</p>
|
|
442
|
+
* {/if}
|
|
443
|
+
* {#if form.rootError}
|
|
444
|
+
* <p role="alert">{form.rootError}</p>
|
|
445
|
+
* {/if}
|
|
446
|
+
* <button disabled={form.submitting}>Log in</button>
|
|
447
|
+
* </form>
|
|
448
|
+
* ```
|
|
449
|
+
*
|
|
450
|
+
* @see {@link https://atom.jarrednorris.dev/forms | Forms}
|
|
451
|
+
* @stability unstable
|
|
452
|
+
* @since 0.4.0
|
|
453
|
+
* @category hooks
|
|
454
|
+
*/
|
|
455
|
+
export declare function useForm<F extends Form.Form<Schema.Top>, A, E, W, Args = undefined>(form: F, options: Form.InstanceOptions<Form.Encoded<F>, Form.Decoded<F>, A, E, W, Args>): Form.Instance<Form.Encoded<F>, Form.Decoded<F>, A, E, Args>;
|
|
456
|
+
export declare function useForm<F extends Form.Form<Schema.Top>>(form: F, options: Form.ValidateOnlyOptions<Form.Encoded<F>>): Form.Instance<Form.Encoded<F>, Form.Decoded<F>, Form.Decoded<F>, never, void>;
|