@connextar/house 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.
Files changed (63) hide show
  1. package/README.md +213 -2
  2. package/dist/house.css +66 -0
  3. package/dist/ux/errors.d.ts +26 -0
  4. package/dist/ux/errors.d.ts.map +1 -0
  5. package/dist/ux/errors.js +33 -0
  6. package/dist/ux/errors.js.map +1 -0
  7. package/dist/ux/feedback.d.ts +23 -0
  8. package/dist/ux/feedback.d.ts.map +1 -0
  9. package/dist/ux/feedback.js +22 -0
  10. package/dist/ux/feedback.js.map +1 -0
  11. package/dist/ux/index.d.ts +20 -0
  12. package/dist/ux/index.d.ts.map +1 -0
  13. package/dist/ux/index.js +20 -0
  14. package/dist/ux/index.js.map +1 -0
  15. package/dist/ux/navigation-index.d.ts +10 -0
  16. package/dist/ux/navigation-index.d.ts.map +1 -0
  17. package/dist/ux/navigation-index.js +10 -0
  18. package/dist/ux/navigation-index.js.map +1 -0
  19. package/dist/ux/navigation-progress.d.ts +53 -0
  20. package/dist/ux/navigation-progress.d.ts.map +1 -0
  21. package/dist/ux/navigation-progress.js +245 -0
  22. package/dist/ux/navigation-progress.js.map +1 -0
  23. package/dist/ux/optimistic.d.ts +139 -0
  24. package/dist/ux/optimistic.d.ts.map +1 -0
  25. package/dist/ux/optimistic.js +242 -0
  26. package/dist/ux/optimistic.js.map +1 -0
  27. package/dist/ux/use-action.d.ts +60 -0
  28. package/dist/ux/use-action.d.ts.map +1 -0
  29. package/dist/ux/use-action.js +100 -0
  30. package/dist/ux/use-action.js.map +1 -0
  31. package/dist/ux/use-optimistic-list.d.ts +69 -0
  32. package/dist/ux/use-optimistic-list.d.ts.map +1 -0
  33. package/dist/ux/use-optimistic-list.js +115 -0
  34. package/dist/ux/use-optimistic-list.js.map +1 -0
  35. package/dist/ux/use-optimistic-value.d.ts +40 -0
  36. package/dist/ux/use-optimistic-value.d.ts.map +1 -0
  37. package/dist/ux/use-optimistic-value.js +68 -0
  38. package/dist/ux/use-optimistic-value.js.map +1 -0
  39. package/dist/wizard/index.d.ts +26 -0
  40. package/dist/wizard/index.d.ts.map +1 -0
  41. package/dist/wizard/index.js +26 -0
  42. package/dist/wizard/index.js.map +1 -0
  43. package/dist/wizard/invalid.d.ts +91 -0
  44. package/dist/wizard/invalid.d.ts.map +1 -0
  45. package/dist/wizard/invalid.js +121 -0
  46. package/dist/wizard/invalid.js.map +1 -0
  47. package/dist/wizard/react-hook-form-index.d.ts +12 -0
  48. package/dist/wizard/react-hook-form-index.d.ts.map +1 -0
  49. package/dist/wizard/react-hook-form-index.js +12 -0
  50. package/dist/wizard/react-hook-form-index.js.map +1 -0
  51. package/dist/wizard/rhf-adapter.d.ts +32 -0
  52. package/dist/wizard/rhf-adapter.d.ts.map +1 -0
  53. package/dist/wizard/rhf-adapter.js +58 -0
  54. package/dist/wizard/rhf-adapter.js.map +1 -0
  55. package/dist/wizard/rules.d.ts +92 -0
  56. package/dist/wizard/rules.d.ts.map +1 -0
  57. package/dist/wizard/rules.js +105 -0
  58. package/dist/wizard/rules.js.map +1 -0
  59. package/dist/wizard/wizard.d.ts +127 -0
  60. package/dist/wizard/wizard.d.ts.map +1 -0
  61. package/dist/wizard/wizard.js +195 -0
  62. package/dist/wizard/wizard.js.map +1 -0
  63. package/package.json +24 -3
@@ -0,0 +1,91 @@
1
+ import { type ActionReporter } from "../ux/feedback.js";
2
+ /**
3
+ * Saying why a form refused, when the reason is somewhere nobody can look.
4
+ *
5
+ * A wizard keeps fields on steps that are mounted but hidden, and a form library
6
+ * will not call a submit handler while any field is invalid. An error shown
7
+ * beside its field speaks for itself. One on a field nobody can see — another
8
+ * step, a field the form hides because it does not apply, a rule that belongs to
9
+ * no field at all — makes the button look broken, and that one has to be said
10
+ * out loud. This is the half of the wizard that has to read the DOM, so it is
11
+ * kept apart from the rules.
12
+ *
13
+ * Where it is said is the app's decision, not this package's: the message goes to
14
+ * the `ActionFeedbackProvider` that `@connextar/house/ux` already defines, so an
15
+ * app wires one reporter to whatever it uses to tell people things and this
16
+ * module imports no toast library.
17
+ */
18
+ /**
19
+ * The attribute a field's error message carries, so a visible one can be told
20
+ * from a hidden one. `FieldError` sets it; an app's own message component sets
21
+ * it too (`<p data-error-for="title">`), which is the whole integration.
22
+ */
23
+ export declare const ERROR_ATTRIBUTE = "data-error-for";
24
+ /** How long to wait before looking. */
25
+ export declare const REPORT_DELAY = 50;
26
+ export interface InvalidMessages {
27
+ /** The heading. */
28
+ title: string;
29
+ /** Used when no problem carried a message of its own. */
30
+ fallback: string;
31
+ }
32
+ export declare const INVALID_MESSAGES: InvalidMessages;
33
+ export interface FieldProblem {
34
+ name: string;
35
+ message: string | null;
36
+ }
37
+ /**
38
+ * The problems in an error object, whatever shape the form library gave them.
39
+ * Only the entries that are objects count: a form library's error map holds one
40
+ * per field, and anything else in there is not a field error.
41
+ */
42
+ export declare function fieldProblems(errors: object): FieldProblem[];
43
+ /**
44
+ * Rendered, outside a hidden step, and — where the page has layout at all —
45
+ * taking up space.
46
+ *
47
+ * A DOM without layout, like a test's, has no boxes to measure, so `hidden`
48
+ * decides there on its own. Measuring alone was AltEd's version, and under jsdom
49
+ * it called every message invisible and said everything twice.
50
+ */
51
+ export declare function isOnScreen(element: HTMLElement): boolean;
52
+ /** The field names whose message is on screen right now. */
53
+ export declare function shownErrorFields(root: ParentNode): string[];
54
+ /**
55
+ * The problems with nothing on screen to show them.
56
+ *
57
+ * A name matches a shown message exactly, or as its prefix: an error on
58
+ * `times` is answered by a message on `times.0.start`, because that is where a
59
+ * form library puts the message for an array field's row.
60
+ */
61
+ export declare function unseenProblems(problems: readonly FieldProblem[], shown: readonly string[]): FieldProblem[];
62
+ export type InvalidReporter = (errors: object) => void;
63
+ /**
64
+ * Report the errors an invalid form refused on, if any of them has nowhere to
65
+ * show itself.
66
+ *
67
+ * Deferred, because it has to run after the form has re-rendered with its errors
68
+ * and after a wizard has moved to the failing step — before that, every message
69
+ * is still hidden and it would say everything twice. Returns a way to cancel,
70
+ * for a component that unmounts in between.
71
+ *
72
+ * Two silences and one noise:
73
+ *
74
+ * - Problems, all of them showing → quiet. The fields say it better.
75
+ * - Problems, at least one unseen → the first unseen message, or the fallback.
76
+ * - **No problems at all → the fallback**, not quiet. A form that refuses with
77
+ * an empty error map is the worst of the cases, not the mildest: something
78
+ * said no and named nothing, and the person is left pressing a dead button.
79
+ */
80
+ export declare function reportInvalid(errors: object, report: ActionReporter, options?: {
81
+ delay?: number;
82
+ root?: ParentNode;
83
+ messages?: Partial<InvalidMessages>;
84
+ }): () => void;
85
+ /**
86
+ * `reportInvalid` bound to the app's reporter — pass it straight to a form
87
+ * library's invalid handler (`handleSubmit(onSubmit, report)`), or let
88
+ * `stepValidator` from `@connextar/house/wizard/react-hook-form` call it.
89
+ */
90
+ export declare function useInvalidReporter(messages?: Partial<InvalidMessages>): InvalidReporter;
91
+ //# sourceMappingURL=invalid.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"invalid.d.ts","sourceRoot":"","sources":["../../src/wizard/invalid.ts"],"names":[],"mappings":"AAIA,OAAO,EAAqB,KAAK,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAE3E;;;;;;;;;;;;;;;GAeG;AAEH;;;;GAIG;AACH,eAAO,MAAM,eAAe,mBAAmB,CAAC;AAEhD,uCAAuC;AACvC,eAAO,MAAM,YAAY,KAAK,CAAC;AAE/B,MAAM,WAAW,eAAe;IAC9B,mBAAmB;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,yDAAyD;IACzD,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,eAAO,MAAM,gBAAgB,EAAE,eAG9B,CAAC;AAEF,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;CACxB;AAED;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,MAAM,EAAE,MAAM,GAAG,YAAY,EAAE,CAM5D;AAED;;;;;;;GAOG;AACH,wBAAgB,UAAU,CAAC,OAAO,EAAE,WAAW,GAAG,OAAO,CAIxD;AAED,4DAA4D;AAC5D,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,UAAU,GAAG,MAAM,EAAE,CAI3D;AAED;;;;;;GAMG;AACH,wBAAgB,cAAc,CAAC,QAAQ,EAAE,SAAS,YAAY,EAAE,EAAE,KAAK,EAAE,SAAS,MAAM,EAAE,GAAG,YAAY,EAAE,CAE1G;AAED,MAAM,MAAM,eAAe,GAAG,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;AAEvD;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,aAAa,CAC3B,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,cAAc,EACtB,OAAO,GAAE;IAAE,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,UAAU,CAAC;IAAC,QAAQ,CAAC,EAAE,OAAO,CAAC,eAAe,CAAC,CAAA;CAAO,GACvF,MAAM,IAAI,CAcZ;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,QAAQ,CAAC,EAAE,OAAO,CAAC,eAAe,CAAC,GAAG,eAAe,CAYvF"}
@@ -0,0 +1,121 @@
1
+ "use client";
2
+ import * as React from "react";
3
+ import { useActionReporter } from "../ux/feedback.js";
4
+ /**
5
+ * Saying why a form refused, when the reason is somewhere nobody can look.
6
+ *
7
+ * A wizard keeps fields on steps that are mounted but hidden, and a form library
8
+ * will not call a submit handler while any field is invalid. An error shown
9
+ * beside its field speaks for itself. One on a field nobody can see — another
10
+ * step, a field the form hides because it does not apply, a rule that belongs to
11
+ * no field at all — makes the button look broken, and that one has to be said
12
+ * out loud. This is the half of the wizard that has to read the DOM, so it is
13
+ * kept apart from the rules.
14
+ *
15
+ * Where it is said is the app's decision, not this package's: the message goes to
16
+ * the `ActionFeedbackProvider` that `@connextar/house/ux` already defines, so an
17
+ * app wires one reporter to whatever it uses to tell people things and this
18
+ * module imports no toast library.
19
+ */
20
+ /**
21
+ * The attribute a field's error message carries, so a visible one can be told
22
+ * from a hidden one. `FieldError` sets it; an app's own message component sets
23
+ * it too (`<p data-error-for="title">`), which is the whole integration.
24
+ */
25
+ export const ERROR_ATTRIBUTE = "data-error-for";
26
+ /** How long to wait before looking. */
27
+ export const REPORT_DELAY = 50;
28
+ export const INVALID_MESSAGES = {
29
+ title: "That can't be saved yet",
30
+ fallback: "Something on the form needs changing — check the highlighted fields.",
31
+ };
32
+ /**
33
+ * The problems in an error object, whatever shape the form library gave them.
34
+ * Only the entries that are objects count: a form library's error map holds one
35
+ * per field, and anything else in there is not a field error.
36
+ */
37
+ export function fieldProblems(errors) {
38
+ return Object.entries(errors).flatMap(([name, value]) => {
39
+ if (typeof value !== "object" || value === null)
40
+ return [];
41
+ const message = value.message;
42
+ return [{ name, message: typeof message === "string" && message !== "" ? message : null }];
43
+ });
44
+ }
45
+ /**
46
+ * Rendered, outside a hidden step, and — where the page has layout at all —
47
+ * taking up space.
48
+ *
49
+ * A DOM without layout, like a test's, has no boxes to measure, so `hidden`
50
+ * decides there on its own. Measuring alone was AltEd's version, and under jsdom
51
+ * it called every message invisible and said everything twice.
52
+ */
53
+ export function isOnScreen(element) {
54
+ if (element.closest("[hidden]"))
55
+ return false;
56
+ const hasLayout = element.ownerDocument.documentElement.getClientRects().length > 0;
57
+ return !hasLayout || element.getClientRects().length > 0;
58
+ }
59
+ /** The field names whose message is on screen right now. */
60
+ export function shownErrorFields(root) {
61
+ return [...root.querySelectorAll(`[${ERROR_ATTRIBUTE}]`)]
62
+ .filter(isOnScreen)
63
+ .map((element) => element.getAttribute(ERROR_ATTRIBUTE) ?? "");
64
+ }
65
+ /**
66
+ * The problems with nothing on screen to show them.
67
+ *
68
+ * A name matches a shown message exactly, or as its prefix: an error on
69
+ * `times` is answered by a message on `times.0.start`, because that is where a
70
+ * form library puts the message for an array field's row.
71
+ */
72
+ export function unseenProblems(problems, shown) {
73
+ return problems.filter(({ name }) => !shown.some((on) => on === name || on.startsWith(`${name}.`)));
74
+ }
75
+ /**
76
+ * Report the errors an invalid form refused on, if any of them has nowhere to
77
+ * show itself.
78
+ *
79
+ * Deferred, because it has to run after the form has re-rendered with its errors
80
+ * and after a wizard has moved to the failing step — before that, every message
81
+ * is still hidden and it would say everything twice. Returns a way to cancel,
82
+ * for a component that unmounts in between.
83
+ *
84
+ * Two silences and one noise:
85
+ *
86
+ * - Problems, all of them showing → quiet. The fields say it better.
87
+ * - Problems, at least one unseen → the first unseen message, or the fallback.
88
+ * - **No problems at all → the fallback**, not quiet. A form that refuses with
89
+ * an empty error map is the worst of the cases, not the mildest: something
90
+ * said no and named nothing, and the person is left pressing a dead button.
91
+ */
92
+ export function reportInvalid(errors, report, options = {}) {
93
+ const { delay = REPORT_DELAY, messages } = options;
94
+ const words = { ...INVALID_MESSAGES, ...messages };
95
+ const problems = fieldProblems(errors);
96
+ const timer = setTimeout(() => {
97
+ const root = options.root ?? globalThis.document;
98
+ const unseen = unseenProblems(problems, shownErrorFields(root));
99
+ if (problems.length > 0 && unseen.length === 0)
100
+ return;
101
+ const message = unseen.map((problem) => problem.message).find((text) => text !== null);
102
+ report({ tone: "error", title: words.title, description: message ?? words.fallback });
103
+ }, delay);
104
+ return () => clearTimeout(timer);
105
+ }
106
+ /**
107
+ * `reportInvalid` bound to the app's reporter — pass it straight to a form
108
+ * library's invalid handler (`handleSubmit(onSubmit, report)`), or let
109
+ * `stepValidator` from `@connextar/house/wizard/react-hook-form` call it.
110
+ */
111
+ export function useInvalidReporter(messages) {
112
+ const report = useActionReporter();
113
+ const title = messages?.title;
114
+ const fallback = messages?.fallback;
115
+ return React.useCallback((errors) => {
116
+ reportInvalid(errors, report, {
117
+ messages: { ...(title === undefined ? {} : { title }), ...(fallback === undefined ? {} : { fallback }) },
118
+ });
119
+ }, [report, title, fallback]);
120
+ }
121
+ //# sourceMappingURL=invalid.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"invalid.js","sourceRoot":"","sources":["../../src/wizard/invalid.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,12 @@
1
+ /**
2
+ * `@connextar/house/wizard/react-hook-form` — the step check for a
3
+ * react-hook-form wizard.
4
+ *
5
+ * Its own entry point so that `react-hook-form` stays an optional peer
6
+ * dependency: every import of it in here is `import type`, which erases, but a
7
+ * declaration file that names a package an app has not installed still fails
8
+ * that app's typecheck. An app on plain state imports `@connextar/house/wizard`
9
+ * and never comes near this.
10
+ */
11
+ export { stepValidator, useStepValidator } from "./rhf-adapter.js";
12
+ //# sourceMappingURL=react-hook-form-index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"react-hook-form-index.d.ts","sourceRoot":"","sources":["../../src/wizard/react-hook-form-index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,OAAO,EAAE,aAAa,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC"}
@@ -0,0 +1,12 @@
1
+ /**
2
+ * `@connextar/house/wizard/react-hook-form` — the step check for a
3
+ * react-hook-form wizard.
4
+ *
5
+ * Its own entry point so that `react-hook-form` stays an optional peer
6
+ * dependency: every import of it in here is `import type`, which erases, but a
7
+ * declaration file that names a package an app has not installed still fails
8
+ * that app's typecheck. An app on plain state imports `@connextar/house/wizard`
9
+ * and never comes near this.
10
+ */
11
+ export { stepValidator, useStepValidator } from "./rhf-adapter.js";
12
+ //# sourceMappingURL=react-hook-form-index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"react-hook-form-index.js","sourceRoot":"","sources":["../../src/wizard/react-hook-form-index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,OAAO,EAAE,aAAa,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC"}
@@ -0,0 +1,32 @@
1
+ import type { FieldValues, UseFormReturn } from "react-hook-form";
2
+ import { type InvalidReporter } from "./invalid.js";
3
+ import type { StepCheck } from "./rules.js";
4
+ /**
5
+ * `@connextar/house/wizard/react-hook-form` — the step check most of the cohort
6
+ * wants, for the library most of the cohort uses.
7
+ *
8
+ * Its own entry point, and the reason is types rather than code: every import
9
+ * from `react-hook-form` here is `import type`, so the emitted JavaScript
10
+ * mentions it nowhere and an app without it can still run every part of
11
+ * `@connextar/house/wizard`. What an app without it *cannot* do is typecheck a
12
+ * declaration file that names the package — so the names live behind a subpath
13
+ * nobody has to reach for. MAS has no `react-hook-form` and never will;
14
+ * `entry-points.test.ts` keeps this split honest.
15
+ */
16
+ /**
17
+ * The usual step check for a react-hook-form wizard: validate just this step's
18
+ * fields, and when one fails, say why if its message is not on screen.
19
+ *
20
+ * Returns `false` rather than a sentence, because a form library puts its
21
+ * messages beside the fields they belong to and a second copy in a banner above
22
+ * them is noise. The exception is the message with nowhere to go, which is what
23
+ * the reporter is for.
24
+ */
25
+ export declare function stepValidator<T extends FieldValues>(form: UseFormReturn<T>, report: InvalidReporter): StepCheck;
26
+ /**
27
+ * `stepValidator` wired to the app's `ActionFeedbackProvider`, with an identity
28
+ * that does not change between renders — a check that is a new function on every
29
+ * render makes the wizard re-render every step's content for nothing.
30
+ */
31
+ export declare function useStepValidator<T extends FieldValues>(form: UseFormReturn<T>): StepCheck;
32
+ //# sourceMappingURL=rhf-adapter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rhf-adapter.d.ts","sourceRoot":"","sources":["../../src/wizard/rhf-adapter.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAa,WAAW,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAE7E,OAAO,EAAsB,KAAK,eAAe,EAAE,MAAM,cAAc,CAAC;AACxE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAE5C;;;;;;;;;;;GAWG;AAEH;;;;;;;;GAQG;AACH,wBAAgB,aAAa,CAAC,CAAC,SAAS,WAAW,EAAE,IAAI,EAAE,aAAa,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,eAAe,GAAG,SAAS,CAgB/G;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,CAAC,SAAS,WAAW,EAAE,IAAI,EAAE,aAAa,CAAC,CAAC,CAAC,GAAG,SAAS,CAazF"}
@@ -0,0 +1,58 @@
1
+ "use client";
2
+ import * as React from "react";
3
+ import { useInvalidReporter } from "./invalid.js";
4
+ /**
5
+ * `@connextar/house/wizard/react-hook-form` — the step check most of the cohort
6
+ * wants, for the library most of the cohort uses.
7
+ *
8
+ * Its own entry point, and the reason is types rather than code: every import
9
+ * from `react-hook-form` here is `import type`, so the emitted JavaScript
10
+ * mentions it nowhere and an app without it can still run every part of
11
+ * `@connextar/house/wizard`. What an app without it *cannot* do is typecheck a
12
+ * declaration file that names the package — so the names live behind a subpath
13
+ * nobody has to reach for. MAS has no `react-hook-form` and never will;
14
+ * `entry-points.test.ts` keeps this split honest.
15
+ */
16
+ /**
17
+ * The usual step check for a react-hook-form wizard: validate just this step's
18
+ * fields, and when one fails, say why if its message is not on screen.
19
+ *
20
+ * Returns `false` rather than a sentence, because a form library puts its
21
+ * messages beside the fields they belong to and a second copy in a banner above
22
+ * them is noise. The exception is the message with nowhere to go, which is what
23
+ * the reporter is for.
24
+ */
25
+ export function stepValidator(form, report) {
26
+ return async (fields) => {
27
+ if (fields.length === 0)
28
+ return true;
29
+ const names = fields;
30
+ const ok = await form.trigger(names);
31
+ if (ok)
32
+ return true;
33
+ report(Object.fromEntries(names.flatMap((name) => {
34
+ const error = form.getFieldState(name).error;
35
+ return error ? [[name, error]] : [];
36
+ })));
37
+ return false;
38
+ };
39
+ }
40
+ /**
41
+ * `stepValidator` wired to the app's `ActionFeedbackProvider`, with an identity
42
+ * that does not change between renders — a check that is a new function on every
43
+ * render makes the wizard re-render every step's content for nothing.
44
+ */
45
+ export function useStepValidator(form) {
46
+ const report = useInvalidReporter();
47
+ // Written after the render, so the check always reads the current form and
48
+ // reporter without itself having to change.
49
+ const latest = React.useRef({ form, report });
50
+ React.useEffect(() => {
51
+ latest.current = { form, report };
52
+ });
53
+ return React.useCallback((fields, step) => {
54
+ const { form: current, report: tell } = latest.current;
55
+ return stepValidator(current, tell)(fields, step);
56
+ }, []);
57
+ }
58
+ //# sourceMappingURL=rhf-adapter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rhf-adapter.js","sourceRoot":"","sources":["../../src/wizard/rhf-adapter.ts"],"names":[],"mappings":"AAAA,YAAY,CAAC;AAEb,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAG/B,OAAO,EAAE,kBAAkB,EAAwB,MAAM,cAAc,CAAC;AAGxE;;;;;;;;;;;GAWG;AAEH;;;;;;;;GAQG;AACH,MAAM,UAAU,aAAa,CAAwB,IAAsB,EAAE,MAAuB;IAClG,OAAO,KAAK,EAAE,MAAM,EAAE,EAAE;QACtB,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO,IAAI,CAAC;QACrC,MAAM,KAAK,GAAG,MAAwB,CAAC;QACvC,MAAM,EAAE,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;QACrC,IAAI,EAAE;YAAE,OAAO,IAAI,CAAC;QACpB,MAAM,CACJ,MAAM,CAAC,WAAW,CAChB,KAAK,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,EAAE;YACrB,MAAM,KAAK,GAAG,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC;YAC7C,OAAO,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,KAAK,CAAU,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;QAC/C,CAAC,CAAC,CACH,CACF,CAAC;QACF,OAAO,KAAK,CAAC;IACf,CAAC,CAAC;AACJ,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,gBAAgB,CAAwB,IAAsB;IAC5E,MAAM,MAAM,GAAG,kBAAkB,EAAE,CAAC;IACpC,2EAA2E;IAC3E,4CAA4C;IAC5C,MAAM,MAAM,GAAG,KAAK,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,CAAC;IAC9C,KAAK,CAAC,SAAS,CAAC,GAAG,EAAE;QACnB,MAAM,CAAC,OAAO,GAAG,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;IACpC,CAAC,CAAC,CAAC;IAEH,OAAO,KAAK,CAAC,WAAW,CAAY,CAAC,MAAM,EAAE,IAAI,EAAE,EAAE;QACnD,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG,MAAM,CAAC,OAAO,CAAC;QACvD,OAAO,aAAa,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;IACpD,CAAC,EAAE,EAAE,CAAC,CAAC;AACT,CAAC"}
@@ -0,0 +1,92 @@
1
+ /**
2
+ * The rules a stepped form follows — where you are, where you may go, and what
3
+ * has to pass before you get there. No React and no DOM, so they are tested
4
+ * without a browser and hold whatever draws them.
5
+ *
6
+ * The one decision worth spelling out is what a *check* is. Five apps wrote a
7
+ * wizard and arrived at two different answers: a react-hook-form app asks the
8
+ * form to validate some field names and gets a boolean back, while an app
9
+ * holding its values in plain state knows the sentence it wants to show and
10
+ * returns that. Neither is wrong, and a shared wizard that picked one of them
11
+ * would shut the other out — so a check may answer either way:
12
+ *
13
+ * | Returned | Means |
14
+ * | ---------------------------- | ------------------------------------------------ |
15
+ * | `true`, `null`, `undefined` | The step is complete |
16
+ * | `false` | Not complete; the host is showing why, in place |
17
+ * | a sentence | Not complete, and this is what to say |
18
+ *
19
+ * An empty string counts as complete. It is not part of either contract, and a
20
+ * check that returns one by accident must not be able to wedge a form shut with
21
+ * nothing on screen — which is the whole failure this module exists to prevent.
22
+ */
23
+ /** What a check answers. See the table above. */
24
+ export type StepOutcome = boolean | string | null | undefined | void;
25
+ /** The part of a step these rules need: what it owns, and its own check. */
26
+ export interface StepCheckable {
27
+ /**
28
+ * Field names this step owns. Passed to the form-level check, which is how a
29
+ * react-hook-form host validates one step's worth of fields and no more.
30
+ */
31
+ fields?: readonly string[];
32
+ /**
33
+ * This step's own check, for a host that keeps validation beside the step
34
+ * rather than in one function. Asked before the form-level check.
35
+ */
36
+ validate?: StepCheck;
37
+ }
38
+ /** Is this step complete? Asked before leaving it, and again for every step on submit. */
39
+ export type StepCheck = (fields: string[], step: StepCheckable) => StepOutcome | Promise<StepOutcome>;
40
+ export type StepResult = {
41
+ ok: true;
42
+ } | {
43
+ ok: false;
44
+ message: string | null;
45
+ };
46
+ /** Normalise whatever a check answered. */
47
+ export declare function stepResult(outcome: StepOutcome): StepResult;
48
+ /**
49
+ * Ask everything that has a say about one step. The step's own check first, then
50
+ * the form-level one; the first refusal is the answer, so its message is the one
51
+ * shown rather than the last one computed.
52
+ */
53
+ export declare function checkStep(step: StepCheckable, onValidateStep?: StepCheck): Promise<StepResult>;
54
+ export interface StepFailure {
55
+ index: number;
56
+ message: string | null;
57
+ }
58
+ /**
59
+ * The first step that will not pass, checked in order from the beginning.
60
+ *
61
+ * This is what a submit does, and checking only the last step is the defect
62
+ * every one of the five copies had at some point. Step back by a marker (which
63
+ * does not validate), clear a required field, jump to the end and press Save:
64
+ * the form refuses with the problem on a step nobody is looking at, and the
65
+ * button looks broken. In MAS it was worse — nothing was checked on submit at
66
+ * all, so a second press after a successful create sent a record whose first
67
+ * step had been cleared.
68
+ *
69
+ * In order matters as much as every: the person is sent to the earliest thing
70
+ * that needs fixing, which is the one they will understand.
71
+ */
72
+ export declare function firstFailure<S extends StepCheckable>(steps: readonly S[], onValidateStep?: StepCheck): Promise<StepFailure | null>;
73
+ export type MarkerState = "done" | "current" | "upcoming";
74
+ /** How a marker reads: behind you, where you are, or ahead. */
75
+ export declare function markerState(index: number, current: number): MarkerState;
76
+ /**
77
+ * May this marker be clicked?
78
+ *
79
+ * Anything reached before now, including the step you are on — clicking that one
80
+ * does nothing, and disabling it takes the tracker's "you are here" out of the
81
+ * tab order for no gain. Steps never reached stay locked, because skipping
82
+ * forward past a step that has never been checked is how people arrive at a
83
+ * submit button that will not work with nothing saying why.
84
+ */
85
+ export declare function isReachable(index: number, furthest: number): boolean;
86
+ /** The next index, not past the end. */
87
+ export declare function nextIndex(current: number, total: number): number;
88
+ /** The previous index, not past the beginning. */
89
+ export declare function previousIndex(current: number): number;
90
+ /** How far through, for the apps that show a bar rather than a count. */
91
+ export declare function percentComplete(current: number, total: number): number;
92
+ //# sourceMappingURL=rules.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rules.d.ts","sourceRoot":"","sources":["../../src/wizard/rules.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,iDAAiD;AACjD,MAAM,MAAM,WAAW,GAAG,OAAO,GAAG,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,IAAI,CAAC;AAErE,4EAA4E;AAC5E,MAAM,WAAW,aAAa;IAC5B;;;OAGG;IACH,MAAM,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC3B;;;OAGG;IACH,QAAQ,CAAC,EAAE,SAAS,CAAC;CACtB;AAED,0FAA0F;AAC1F,MAAM,MAAM,SAAS,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,EAAE,IAAI,EAAE,aAAa,KAAK,WAAW,GAAG,OAAO,CAAC,WAAW,CAAC,CAAC;AAEtG,MAAM,MAAM,UAAU,GAAG;IAAE,EAAE,EAAE,IAAI,CAAA;CAAE,GAAG;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,CAAC;AAI9E,2CAA2C;AAC3C,wBAAgB,UAAU,CAAC,OAAO,EAAE,WAAW,GAAG,UAAU,CAI3D;AAED;;;;GAIG;AACH,wBAAsB,SAAS,CAAC,IAAI,EAAE,aAAa,EAAE,cAAc,CAAC,EAAE,SAAS,GAAG,OAAO,CAAC,UAAU,CAAC,CAQpG;AAED,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;CACxB;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAsB,YAAY,CAAC,CAAC,SAAS,aAAa,EACxD,KAAK,EAAE,SAAS,CAAC,EAAE,EACnB,cAAc,CAAC,EAAE,SAAS,GACzB,OAAO,CAAC,WAAW,GAAG,IAAI,CAAC,CAQ7B;AAED,MAAM,MAAM,WAAW,GAAG,MAAM,GAAG,SAAS,GAAG,UAAU,CAAC;AAE1D,+DAA+D;AAC/D,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,WAAW,CAGvE;AAED;;;;;;;;GAQG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAEpE;AAED,wCAAwC;AACxC,wBAAgB,SAAS,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAEhE;AAED,kDAAkD;AAClD,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAErD;AAED,yEAAyE;AACzE,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAGtE"}
@@ -0,0 +1,105 @@
1
+ /**
2
+ * The rules a stepped form follows — where you are, where you may go, and what
3
+ * has to pass before you get there. No React and no DOM, so they are tested
4
+ * without a browser and hold whatever draws them.
5
+ *
6
+ * The one decision worth spelling out is what a *check* is. Five apps wrote a
7
+ * wizard and arrived at two different answers: a react-hook-form app asks the
8
+ * form to validate some field names and gets a boolean back, while an app
9
+ * holding its values in plain state knows the sentence it wants to show and
10
+ * returns that. Neither is wrong, and a shared wizard that picked one of them
11
+ * would shut the other out — so a check may answer either way:
12
+ *
13
+ * | Returned | Means |
14
+ * | ---------------------------- | ------------------------------------------------ |
15
+ * | `true`, `null`, `undefined` | The step is complete |
16
+ * | `false` | Not complete; the host is showing why, in place |
17
+ * | a sentence | Not complete, and this is what to say |
18
+ *
19
+ * An empty string counts as complete. It is not part of either contract, and a
20
+ * check that returns one by accident must not be able to wedge a form shut with
21
+ * nothing on screen — which is the whole failure this module exists to prevent.
22
+ */
23
+ const PASSED = { ok: true };
24
+ /** Normalise whatever a check answered. */
25
+ export function stepResult(outcome) {
26
+ if (outcome === false)
27
+ return { ok: false, message: null };
28
+ if (typeof outcome === "string" && outcome !== "")
29
+ return { ok: false, message: outcome };
30
+ return PASSED;
31
+ }
32
+ /**
33
+ * Ask everything that has a say about one step. The step's own check first, then
34
+ * the form-level one; the first refusal is the answer, so its message is the one
35
+ * shown rather than the last one computed.
36
+ */
37
+ export async function checkStep(step, onValidateStep) {
38
+ const fields = [...(step.fields ?? [])];
39
+ for (const check of [step.validate, onValidateStep]) {
40
+ if (!check)
41
+ continue;
42
+ const result = stepResult(await check(fields, step));
43
+ if (!result.ok)
44
+ return result;
45
+ }
46
+ return PASSED;
47
+ }
48
+ /**
49
+ * The first step that will not pass, checked in order from the beginning.
50
+ *
51
+ * This is what a submit does, and checking only the last step is the defect
52
+ * every one of the five copies had at some point. Step back by a marker (which
53
+ * does not validate), clear a required field, jump to the end and press Save:
54
+ * the form refuses with the problem on a step nobody is looking at, and the
55
+ * button looks broken. In MAS it was worse — nothing was checked on submit at
56
+ * all, so a second press after a successful create sent a record whose first
57
+ * step had been cleared.
58
+ *
59
+ * In order matters as much as every: the person is sent to the earliest thing
60
+ * that needs fixing, which is the one they will understand.
61
+ */
62
+ export async function firstFailure(steps, onValidateStep) {
63
+ for (let index = 0; index < steps.length; index++) {
64
+ const step = steps[index];
65
+ if (!step)
66
+ continue;
67
+ const result = await checkStep(step, onValidateStep);
68
+ if (!result.ok)
69
+ return { index, message: result.message };
70
+ }
71
+ return null;
72
+ }
73
+ /** How a marker reads: behind you, where you are, or ahead. */
74
+ export function markerState(index, current) {
75
+ if (index < current)
76
+ return "done";
77
+ return index === current ? "current" : "upcoming";
78
+ }
79
+ /**
80
+ * May this marker be clicked?
81
+ *
82
+ * Anything reached before now, including the step you are on — clicking that one
83
+ * does nothing, and disabling it takes the tracker's "you are here" out of the
84
+ * tab order for no gain. Steps never reached stay locked, because skipping
85
+ * forward past a step that has never been checked is how people arrive at a
86
+ * submit button that will not work with nothing saying why.
87
+ */
88
+ export function isReachable(index, furthest) {
89
+ return index <= furthest;
90
+ }
91
+ /** The next index, not past the end. */
92
+ export function nextIndex(current, total) {
93
+ return Math.min(current + 1, Math.max(total - 1, 0));
94
+ }
95
+ /** The previous index, not past the beginning. */
96
+ export function previousIndex(current) {
97
+ return Math.max(current - 1, 0);
98
+ }
99
+ /** How far through, for the apps that show a bar rather than a count. */
100
+ export function percentComplete(current, total) {
101
+ if (total <= 0)
102
+ return 0;
103
+ return Math.round((Math.min(current + 1, total) / total) * 100);
104
+ }
105
+ //# sourceMappingURL=rules.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rules.js","sourceRoot":"","sources":["../../src/wizard/rules.ts"],"names":[],"mappings":"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"}