@menapo/forms 0.0.0-stage → 0.1.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/LICENSE +21 -0
- package/README.md +45 -2
- package/dist/array.d.ts +5 -0
- package/dist/array.js +101 -0
- package/dist/checks.d.ts +4 -0
- package/dist/checks.js +53 -0
- package/dist/factories/fn.d.ts +8 -0
- package/dist/factories/fn.js +5 -0
- package/dist/factories/route.d.ts +5 -0
- package/dist/factories/route.js +31 -0
- package/dist/form-BZ2OH06z.js +48 -0
- package/dist/glue/preact.d.ts +4 -0
- package/dist/glue/preact.js +59 -0
- package/dist/glue/react.d.ts +4 -0
- package/dist/glue/react.js +55 -0
- package/dist/glue/solid.d.ts +4 -0
- package/dist/glue/solid.js +48 -0
- package/dist/glue/svelte.svelte.d.ts +4 -0
- package/dist/glue/svelte.svelte.js +62 -0
- package/dist/glue/vanilla.d.ts +4 -0
- package/dist/glue/vanilla.js +88 -0
- package/dist/glue/vue.d.ts +4 -0
- package/dist/glue/vue.js +47 -0
- package/dist/index.d.ts +34 -0
- package/dist/index.js +30 -0
- package/dist/instance-C1aijv06.js +683 -0
- package/dist/names-D_TE3BpM.js +62 -0
- package/dist/paths-DqNafpnl.js +73 -0
- package/dist/route/files.d.ts +5 -0
- package/dist/route/files.js +34 -0
- package/dist/server.d.ts +25 -0
- package/dist/server.js +79 -0
- package/dist/source-DdFTPjID.js +15 -0
- package/dist/types-CdCFdlFp.d.ts +200 -0
- package/dist/view--JGPLHEo.js +308 -0
- package/dist/view-5_kyMWP3.d.ts +331 -0
- package/dist/vite.d.ts +20 -0
- package/dist/vite.js +1154 -0
- package/package.json +98 -3
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { a as objectOptions, r as create } from "../view--JGPLHEo.js";
|
|
2
|
+
import { r as lower, t as names } from "../names-D_TE3BpM.js";
|
|
3
|
+
import { t as nextId } from "../source-DdFTPjID.js";
|
|
4
|
+
//#region ../glue/src/vanilla.ts
|
|
5
|
+
/**
|
|
6
|
+
* No framework (machine PLAN §5.5): listeners hear when the snapshot moves,
|
|
7
|
+
* and deferred side effects run right after them, so they find the DOM the
|
|
8
|
+
* listeners updated (D13: in vanilla, right after the event). Sources mount
|
|
9
|
+
* on the next microtask, after the code that created them has had a chance
|
|
10
|
+
* to render them once, and `stop()` is the unmount plain code has none of.
|
|
11
|
+
* A class, so each source costs one object and one closure.
|
|
12
|
+
*/
|
|
13
|
+
/** Sources waiting for the microtask that mounts them. */
|
|
14
|
+
let waiting;
|
|
15
|
+
function mountWaiting() {
|
|
16
|
+
const sources = waiting;
|
|
17
|
+
waiting = void 0;
|
|
18
|
+
for (const source of sources) source.mount();
|
|
19
|
+
}
|
|
20
|
+
var Vanilla = class {
|
|
21
|
+
source;
|
|
22
|
+
/** The snapshot listeners last heard; nothing is computed while none listens. */
|
|
23
|
+
seen = -1;
|
|
24
|
+
/** Copied on write: a change being told keeps the listeners it started with. */
|
|
25
|
+
listeners;
|
|
26
|
+
/** Unsubscribes from the source. */
|
|
27
|
+
off;
|
|
28
|
+
constructor(source) {
|
|
29
|
+
this.source = source;
|
|
30
|
+
this.off = source.subscribe(() => this.tell());
|
|
31
|
+
}
|
|
32
|
+
tell() {
|
|
33
|
+
const { source, listeners } = this;
|
|
34
|
+
if (listeners) {
|
|
35
|
+
const now = source.snapshot();
|
|
36
|
+
if (now !== this.seen) {
|
|
37
|
+
this.seen = now;
|
|
38
|
+
for (const listener of listeners) listener();
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
source.commit();
|
|
42
|
+
}
|
|
43
|
+
/** Calls `listener` after each change the snapshot shows; returns the unsubscribe. */
|
|
44
|
+
subscribe(listener) {
|
|
45
|
+
if (!this.listeners) this.seen = this.source.snapshot();
|
|
46
|
+
this.listeners = this.listeners ? [...this.listeners, listener] : [listener];
|
|
47
|
+
return () => {
|
|
48
|
+
this.listeners = this.listeners?.filter((l) => l !== listener);
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
/** What a framework does on unmount: listeners go, side effects stop, and a source not mounted yet never is. */
|
|
52
|
+
stop() {
|
|
53
|
+
this.listeners = void 0;
|
|
54
|
+
this.off();
|
|
55
|
+
const at = waiting ? waiting.indexOf(this.source) : -1;
|
|
56
|
+
if (at >= 0) waiting.splice(at, 1);
|
|
57
|
+
this.source.unmount();
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
function useSource(create, props) {
|
|
61
|
+
const source = create(nextId(), props);
|
|
62
|
+
if (!waiting) {
|
|
63
|
+
waiting = [];
|
|
64
|
+
queueMicrotask(mountWaiting);
|
|
65
|
+
}
|
|
66
|
+
waiting.push(source);
|
|
67
|
+
return new Vanilla(source);
|
|
68
|
+
}
|
|
69
|
+
//#endregion
|
|
70
|
+
//#region src/glue/vanilla.ts
|
|
71
|
+
/**
|
|
72
|
+
* `useForm` without a framework (PLAN §5.2): `f.subscribe` hears each
|
|
73
|
+
* change the view read, and `spread(el, f.parts.form)` from
|
|
74
|
+
* `@menapo/machine/dom` puts parts on elements. Attributes are lowercase,
|
|
75
|
+
* as the DOM names them (`oninput`, `onfocusout`); the instance mounts on
|
|
76
|
+
* the next microtask, after the code that created it rendered it once.
|
|
77
|
+
*/
|
|
78
|
+
const N = names(lower, false);
|
|
79
|
+
function useForm(source, options) {
|
|
80
|
+
objectOptions(options, "plain code");
|
|
81
|
+
const vanilla = useSource(create(source, N), options);
|
|
82
|
+
const view = vanilla.source;
|
|
83
|
+
view.keep = true;
|
|
84
|
+
view.u = vanilla;
|
|
85
|
+
return view.current();
|
|
86
|
+
}
|
|
87
|
+
//#endregion
|
|
88
|
+
export { useForm };
|
package/dist/glue/vue.js
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { r as create } from "../view--JGPLHEo.js";
|
|
2
|
+
import { a as vue, t as names } from "../names-D_TE3BpM.js";
|
|
3
|
+
import { t as nextId } from "../source-DdFTPjID.js";
|
|
4
|
+
import { nextTick, onBeforeUnmount, onMounted, shallowRef, useId, watch } from "vue";
|
|
5
|
+
//#region ../glue/src/vue.ts
|
|
6
|
+
/**
|
|
7
|
+
* Vue 3.5 (machine PLAN §5.5): a `shallowRef` holding the snapshot, which a
|
|
8
|
+
* render reads through the returned tracker and which a change replaces,
|
|
9
|
+
* so only views that read it update. Props given as a function are watched
|
|
10
|
+
* before each render; side effects start once mounted and run after each
|
|
11
|
+
* flush that follows a change (`nextTick`, queued after Vue's own).
|
|
12
|
+
*/
|
|
13
|
+
/** Returns the tracker: call it in a render or `computed` to update with the source. */
|
|
14
|
+
function useSource(create, props) {
|
|
15
|
+
const read = typeof props === "function" ? props : void 0;
|
|
16
|
+
const source = create(useId() ?? nextId(), read ? read() : props);
|
|
17
|
+
const version = shallowRef(source.snapshot());
|
|
18
|
+
const commit = () => source.commit();
|
|
19
|
+
const off = source.subscribe(() => {
|
|
20
|
+
version.value = source.snapshot();
|
|
21
|
+
nextTick(commit);
|
|
22
|
+
});
|
|
23
|
+
if (read) watch(read, (next) => source.setProps(next), { flush: "pre" });
|
|
24
|
+
onMounted(() => source.mount());
|
|
25
|
+
onBeforeUnmount(() => {
|
|
26
|
+
off();
|
|
27
|
+
source.unmount();
|
|
28
|
+
});
|
|
29
|
+
return [source, () => version.value];
|
|
30
|
+
}
|
|
31
|
+
//#endregion
|
|
32
|
+
//#region src/glue/vue.ts
|
|
33
|
+
/**
|
|
34
|
+
* `useForm` in Vue (PLAN §5.2): the shared glue's `shallowRef` adapter;
|
|
35
|
+
* every read calls the tracker, and options given as a getter are watched,
|
|
36
|
+
* so pristine fields follow new defaults (D11). Handlers are named
|
|
37
|
+
* `onInput`, `onFocusout` (glue's naming).
|
|
38
|
+
*/
|
|
39
|
+
const N = names(vue, false);
|
|
40
|
+
function useForm(source, options) {
|
|
41
|
+
const [view, track] = useSource(create(source, N), options);
|
|
42
|
+
view.k = track;
|
|
43
|
+
view.keep = true;
|
|
44
|
+
return view.current();
|
|
45
|
+
}
|
|
46
|
+
//#endregion
|
|
47
|
+
export { useForm };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { o as form } from "./view-5_kyMWP3.js";
|
|
2
|
+
import { S as UseFormOptions, _ as OutputOf, a as FieldOf, b as ShownError, c as FieldView, d as FormOptions, g as OptionParts, h as InputOf, i as Defaults, l as Fields, m as FormView, n as Attrs, p as FormState, r as Checks, t as ArrayField, u as Form, v as Refreshable, y as RouteFormOptions } from "./types-CdCFdlFp.js";
|
|
3
|
+
//#region src/core/env.d.ts
|
|
4
|
+
/** What DevTools hears (M6): an instance's change, or a submit that ended. */
|
|
5
|
+
type DevtoolsEvent = {
|
|
6
|
+
readonly kind: "change";
|
|
7
|
+
readonly instance: object;
|
|
8
|
+
} | {
|
|
9
|
+
readonly kind: "gone";
|
|
10
|
+
readonly instance: object;
|
|
11
|
+
} | {
|
|
12
|
+
readonly kind: "submit";
|
|
13
|
+
readonly instance: object;
|
|
14
|
+
readonly ms: number;
|
|
15
|
+
readonly error?: unknown;
|
|
16
|
+
};
|
|
17
|
+
//#endregion
|
|
18
|
+
//#region src/core/inspect.d.ts
|
|
19
|
+
/** `listener` hears every change of any rendered form and every submit's end; returns the stop. */
|
|
20
|
+
export declare function inspect(listener: (event: DevtoolsEvent) => void): () => void;
|
|
21
|
+
//#endregion
|
|
22
|
+
//#region src/core/use.d.ts
|
|
23
|
+
interface UseForm {
|
|
24
|
+
/** A parent's view, for a child component: its own subscription to the same form. */
|
|
25
|
+
<F>(view: FormView<F>): FormView<F>;
|
|
26
|
+
/** A form in a component; Vue, Svelte and Solid also take the options as a getter. */
|
|
27
|
+
<F extends {
|
|
28
|
+
readonly name: string;
|
|
29
|
+
}>(form: F, options?: UseFormOptions<InputOf<F>, OutputOf<F>> | (() => UseFormOptions<InputOf<F>, OutputOf<F>>)): FormView<F>;
|
|
30
|
+
}
|
|
31
|
+
/** Reads a form in a component: values, errors, submission and parts (§5.3). */
|
|
32
|
+
export declare const useForm: UseForm;
|
|
33
|
+
//#endregion
|
|
34
|
+
export { type ArrayField, type Attrs, type Checks, type Defaults, type DevtoolsEvent, type FieldOf, type FieldView, type Fields, type Form, type FormOptions, type FormState, type FormView, type OptionParts, type Refreshable, type RouteFormOptions, type ShownError, type UseForm, type UseFormOptions, form };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { i as fail, r as env } from "./instance-C1aijv06.js";
|
|
2
|
+
import { r as form } from "./form-BZ2OH06z.js";
|
|
3
|
+
//#region src/core/inspect.ts
|
|
4
|
+
/**
|
|
5
|
+
* `inspect` (D18): what DevTools reads the forms through, a nullable hook
|
|
6
|
+
* in the core as machine's and query's. An app that never imports it ships
|
|
7
|
+
* none of this; the plugin's dev-only page module is the one caller.
|
|
8
|
+
*/
|
|
9
|
+
/** `listener` hears every change of any rendered form and every submit's end; returns the stop. */
|
|
10
|
+
function inspect(listener) {
|
|
11
|
+
const previous = env.devtools;
|
|
12
|
+
env.devtools = (event) => {
|
|
13
|
+
previous?.(event);
|
|
14
|
+
listener(event);
|
|
15
|
+
};
|
|
16
|
+
return () => void (env.devtools = previous);
|
|
17
|
+
}
|
|
18
|
+
//#endregion
|
|
19
|
+
//#region src/core/use.ts
|
|
20
|
+
/**
|
|
21
|
+
* `useForm` as `@menapo/forms` exports it (PLAN §5.2): the Vite plugin
|
|
22
|
+
* resolves each module's import to its framework's glue (`src/glue/`), so
|
|
23
|
+
* this body runs only when the plugin is missing, and says so.
|
|
24
|
+
*/
|
|
25
|
+
/** Reads a form in a component: values, errors, submission and parts (§5.3). */
|
|
26
|
+
const useForm = (() => {
|
|
27
|
+
throw fail("NO_PLUGIN", "useForm ran without the Vite plugin; add forms() from \"@menapo/forms/vite\" to vite.config.ts, and reach useForm from a module the plugin transforms, by import.");
|
|
28
|
+
});
|
|
29
|
+
//#endregion
|
|
30
|
+
export { form, inspect, useForm };
|