@connextar/house 0.3.0 → 0.5.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 +242 -6
- package/dist/setup-guide/bits.d.ts +89 -0
- package/dist/setup-guide/bits.d.ts.map +1 -0
- package/dist/setup-guide/bits.js +134 -0
- package/dist/setup-guide/bits.js.map +1 -0
- package/dist/setup-guide/card.d.ts +33 -0
- package/dist/setup-guide/card.d.ts.map +1 -0
- package/dist/setup-guide/card.js +56 -0
- package/dist/setup-guide/card.js.map +1 -0
- package/dist/setup-guide/class-names.d.ts +68 -0
- package/dist/setup-guide/class-names.d.ts.map +1 -0
- package/dist/setup-guide/class-names.js +63 -0
- package/dist/setup-guide/class-names.js.map +1 -0
- package/dist/setup-guide/context.d.ts +89 -0
- package/dist/setup-guide/context.d.ts.map +1 -0
- package/dist/setup-guide/context.js +124 -0
- package/dist/setup-guide/context.js.map +1 -0
- package/dist/setup-guide/guide.d.ts +159 -0
- package/dist/setup-guide/guide.d.ts.map +1 -0
- package/dist/setup-guide/guide.js +128 -0
- package/dist/setup-guide/guide.js.map +1 -0
- package/dist/setup-guide/index.d.ts +27 -0
- package/dist/setup-guide/index.d.ts.map +1 -0
- package/dist/setup-guide/index.js +27 -0
- package/dist/setup-guide/index.js.map +1 -0
- package/dist/setup-guide/launcher.d.ts +38 -0
- package/dist/setup-guide/launcher.d.ts.map +1 -0
- package/dist/setup-guide/launcher.js +60 -0
- package/dist/setup-guide/launcher.js.map +1 -0
- package/dist/setup-guide/strip.d.ts +21 -0
- package/dist/setup-guide/strip.d.ts.map +1 -0
- package/dist/setup-guide/strip.js +42 -0
- package/dist/setup-guide/strip.js.map +1 -0
- package/dist/wizard/index.d.ts +26 -0
- package/dist/wizard/index.d.ts.map +1 -0
- package/dist/wizard/index.js +26 -0
- package/dist/wizard/index.js.map +1 -0
- package/dist/wizard/invalid.d.ts +91 -0
- package/dist/wizard/invalid.d.ts.map +1 -0
- package/dist/wizard/invalid.js +121 -0
- package/dist/wizard/invalid.js.map +1 -0
- package/dist/wizard/react-hook-form-index.d.ts +12 -0
- package/dist/wizard/react-hook-form-index.d.ts.map +1 -0
- package/dist/wizard/react-hook-form-index.js +12 -0
- package/dist/wizard/react-hook-form-index.js.map +1 -0
- package/dist/wizard/rhf-adapter.d.ts +32 -0
- package/dist/wizard/rhf-adapter.d.ts.map +1 -0
- package/dist/wizard/rhf-adapter.js +58 -0
- package/dist/wizard/rhf-adapter.js.map +1 -0
- package/dist/wizard/rules.d.ts +92 -0
- package/dist/wizard/rules.d.ts.map +1 -0
- package/dist/wizard/rules.js +105 -0
- package/dist/wizard/rules.js.map +1 -0
- package/dist/wizard/wizard.d.ts +127 -0
- package/dist/wizard/wizard.d.ts.map +1 -0
- package/dist/wizard/wizard.js +195 -0
- package/dist/wizard/wizard.js.map +1 -0
- package/package.json +19 -2
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What a new account is asked to do, how far through it is, and what that has
|
|
3
|
+
* earned. No React and no DOM, so the rules are tested without a browser and
|
|
4
|
+
* hold whatever draws them.
|
|
5
|
+
*
|
|
6
|
+
* **Nothing about progress is stored.** A step is *done* when the account holds
|
|
7
|
+
* what it asks for — a learner, a price, a published course — however it got
|
|
8
|
+
* there; the host reads that fresh each time. What *is* stored is the reward:
|
|
9
|
+
* one entry per step per account, keyed on the step, so a step pays once
|
|
10
|
+
* however many times its thing is made, deleted and made again. That split is
|
|
11
|
+
* the whole design, and it is why `done` and `earned` arrive here as two
|
|
12
|
+
* separate sets rather than one status per step.
|
|
13
|
+
*
|
|
14
|
+
* A step key is permanent. Rewards are keyed on it, so renaming one pays
|
|
15
|
+
* everybody a second time.
|
|
16
|
+
*/
|
|
17
|
+
const rewardOf = (step) => step.reward ?? 0;
|
|
18
|
+
const sum = (numbers) => numbers.reduce((total, n) => total + n, 0);
|
|
19
|
+
/** Every step of every item, in order. */
|
|
20
|
+
export function stepsOf(items) {
|
|
21
|
+
return items.flatMap((item) => [...item.steps]);
|
|
22
|
+
}
|
|
23
|
+
/** Every step key, for checking what a request names before trusting it. */
|
|
24
|
+
export function stepKeysOf(items) {
|
|
25
|
+
return new Set(stepsOf(items).map((step) => step.key));
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Whether an item can be started yet.
|
|
29
|
+
*
|
|
30
|
+
* Only while nothing of it is done: an item that somehow got started stays
|
|
31
|
+
* open, because telling somebody they cannot do a thing they have half done is
|
|
32
|
+
* worse than a requirement quietly not applying.
|
|
33
|
+
*/
|
|
34
|
+
function blockedBy(item, done, doneCount) {
|
|
35
|
+
if (doneCount > 0 || !item.requires?.length)
|
|
36
|
+
return null;
|
|
37
|
+
const unmet = item.requires.some((key) => !done.has(key));
|
|
38
|
+
return unmet ? (item.blockedLabel ?? "Not yet") : null;
|
|
39
|
+
}
|
|
40
|
+
/** The guide as it stands for one account. Pure: the same inputs always give the same answer. */
|
|
41
|
+
export function buildGuide({ pathway, items, done, earned, hidden = false }) {
|
|
42
|
+
const built = items.map((def) => {
|
|
43
|
+
const steps = def.steps.map((step) => ({
|
|
44
|
+
...step,
|
|
45
|
+
reward: rewardOf(step),
|
|
46
|
+
done: done.has(step.key),
|
|
47
|
+
earned: earned.has(step.key),
|
|
48
|
+
}));
|
|
49
|
+
const doneCount = steps.filter((step) => step.done).length;
|
|
50
|
+
return {
|
|
51
|
+
...def,
|
|
52
|
+
steps,
|
|
53
|
+
reward: sum(steps.map((step) => step.reward)),
|
|
54
|
+
earnedReward: sum(steps.filter((step) => step.earned).map((step) => step.reward)),
|
|
55
|
+
doneCount,
|
|
56
|
+
// An item with no steps at all is not "done" — it has nothing to be done about.
|
|
57
|
+
done: steps.length > 0 && doneCount === steps.length,
|
|
58
|
+
blocked: blockedBy(def, done, doneCount),
|
|
59
|
+
};
|
|
60
|
+
});
|
|
61
|
+
const all = built.flatMap((item) => item.steps);
|
|
62
|
+
return {
|
|
63
|
+
pathway,
|
|
64
|
+
items: built,
|
|
65
|
+
totalReward: sum(all.map((step) => step.reward)),
|
|
66
|
+
earnedReward: sum(all.filter((step) => step.earned).map((step) => step.reward)),
|
|
67
|
+
doneItems: built.filter((item) => item.done).length,
|
|
68
|
+
nextKey: built.find((item) => !item.done && !item.blocked)?.key ?? null,
|
|
69
|
+
complete: all.length > 0 && all.every((step) => step.done && step.earned),
|
|
70
|
+
pendingKeys: all.filter((step) => step.done && !step.earned).map((step) => step.key),
|
|
71
|
+
hidden,
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
export function findItem(guide, key) {
|
|
75
|
+
return guide?.items.find((item) => item.key === key) ?? null;
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* What counts as done: what the records show now, plus everything already paid
|
|
79
|
+
* for.
|
|
80
|
+
*
|
|
81
|
+
* A reward, once earned, is not taken back — so a step whose thing has since
|
|
82
|
+
* been deleted stays ticked rather than reopening and asking to be paid a
|
|
83
|
+
* second time. Hosts pass the result of this as `done`.
|
|
84
|
+
*/
|
|
85
|
+
export function doneSteps(facts, earned) {
|
|
86
|
+
return new Set([...facts, ...earned]);
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* The steps this account has done and not been paid for — what a sync owes it.
|
|
90
|
+
*
|
|
91
|
+
* Kept here, away from the database, because *which* steps are owed is a rule
|
|
92
|
+
* and writing the entries is a transaction. The host loops over this and writes
|
|
93
|
+
* one row each, letting its unique index refuse a second payment: two tabs
|
|
94
|
+
* asking at the same moment both get this list, and only one of them gets to
|
|
95
|
+
* write each row.
|
|
96
|
+
*/
|
|
97
|
+
export function rewardsOwed(items, done, earned) {
|
|
98
|
+
return stepsOf(items)
|
|
99
|
+
.filter((step) => done.has(step.key) && !earned.has(step.key) && rewardOf(step) > 0)
|
|
100
|
+
.map((step) => ({ key: step.key, title: step.title, reward: rewardOf(step) }));
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* How far through one item is.
|
|
104
|
+
*
|
|
105
|
+
* `ready` is for a flow that saves nothing until its last step: its choices are
|
|
106
|
+
* made but nothing exists yet, so they tick without being done. They count
|
|
107
|
+
* towards the bar — the point of the bar is to show the end approaching — but
|
|
108
|
+
* never towards what has been earned.
|
|
109
|
+
*/
|
|
110
|
+
export function itemProgress(item, ready) {
|
|
111
|
+
const isReady = (step) => !step.done && Boolean(ready?.has(step.key));
|
|
112
|
+
const progressed = item.steps.filter((step) => step.done || isReady(step)).length;
|
|
113
|
+
const total = item.steps.length;
|
|
114
|
+
return {
|
|
115
|
+
progressed,
|
|
116
|
+
total,
|
|
117
|
+
percent: total === 0 ? 0 : Math.round((progressed / total) * 100),
|
|
118
|
+
nextKey: item.steps.find((step) => !step.done && !isReady(step))?.key ?? null,
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
export function stepState(step, nextKey, ready) {
|
|
122
|
+
if (step.done)
|
|
123
|
+
return "done";
|
|
124
|
+
if (ready?.has(step.key))
|
|
125
|
+
return "ready";
|
|
126
|
+
return step.key === nextKey ? "next" : "todo";
|
|
127
|
+
}
|
|
128
|
+
//# sourceMappingURL=guide.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"guide.js","sourceRoot":"","sources":["../../src/setup-guide/guide.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `@connextar/house/setup-guide` — the checklist a new account works through,
|
|
3
|
+
* and what finishing a step earns.
|
|
4
|
+
*
|
|
5
|
+
* It exists because the hard parts are not the checklist. They are: knowing
|
|
6
|
+
* what is done without storing it (so a thing made any other way still counts),
|
|
7
|
+
* paying for it exactly once however many tabs ask at the same moment, and
|
|
8
|
+
* saying so while the person is still looking at what they just did. Those are
|
|
9
|
+
* the same three problems in every app, and they have one right answer each.
|
|
10
|
+
*
|
|
11
|
+
* What the host supplies is everything that is genuinely its own: the steps,
|
|
12
|
+
* how each one is read from its records, where the reward is written, and what
|
|
13
|
+
* a reward is even called. This package holds no routes, makes no requests,
|
|
14
|
+
* imports no toast library and knows nothing about a database.
|
|
15
|
+
*
|
|
16
|
+
* The three surfaces are a card for a home page, a strip for the screen where
|
|
17
|
+
* an item's steps are actually done, and a launcher for the header. The panel
|
|
18
|
+
* the launcher opens is the app's own sheet — see `launcher.tsx`.
|
|
19
|
+
*/
|
|
20
|
+
export { buildGuide, doneSteps, findItem, itemProgress, rewardsOwed, stepKeysOf, stepState, stepsOf, type BuildGuideInput, type GuideItem, type GuideStep, type ItemProgress, type SetupGuide, type SetupGuideLink, type SetupHowTo, type SetupItemDef, type SetupReward, type SetupStepDef, type StepState, } from "./guide.js";
|
|
21
|
+
export { DEFAULT_REWARD_WORDS, rewardWord, SetupGuideProvider, useSetupGuide, type RewardWords, type SetupGuideProviderProps, type SetupGuideState, type SetupGuideTransport, type SetupLinkComponent, type SetupLinkProps, type SetupSyncResult, } from "./context.js";
|
|
22
|
+
export { SETUP_BAND_STYLE, SETUP_GUIDE_CLASS_NAMES, type SetupGuideClassNames } from "./class-names.js";
|
|
23
|
+
export { GuideIconLink, HowText, HowToButton, ItemHelp, MiniProgress, ProgressRing, RewardChip, RewardRing, Segments, StepCircle, useClassNames, type ClassNameOverrides, type RewardState, } from "./bits.js";
|
|
24
|
+
export { SetupCard, type SetupActions, type SetupCardProps } from "./card.js";
|
|
25
|
+
export { SetupStrip, type SetupStripProps } from "./strip.js";
|
|
26
|
+
export { SetupGuideList, SetupGuideSummary, SetupLauncherButton } from "./launcher.js";
|
|
27
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/setup-guide/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AACH,OAAO,EACL,UAAU,EACV,SAAS,EACT,QAAQ,EACR,YAAY,EACZ,WAAW,EACX,UAAU,EACV,SAAS,EACT,OAAO,EACP,KAAK,eAAe,EACpB,KAAK,SAAS,EACd,KAAK,SAAS,EACd,KAAK,YAAY,EACjB,KAAK,UAAU,EACf,KAAK,cAAc,EACnB,KAAK,UAAU,EACf,KAAK,YAAY,EACjB,KAAK,WAAW,EAChB,KAAK,YAAY,EACjB,KAAK,SAAS,GACf,MAAM,YAAY,CAAC;AAEpB,OAAO,EACL,oBAAoB,EACpB,UAAU,EACV,kBAAkB,EAClB,aAAa,EACb,KAAK,WAAW,EAChB,KAAK,uBAAuB,EAC5B,KAAK,eAAe,EACpB,KAAK,mBAAmB,EACxB,KAAK,kBAAkB,EACvB,KAAK,cAAc,EACnB,KAAK,eAAe,GACrB,MAAM,cAAc,CAAC;AAEtB,OAAO,EAAE,gBAAgB,EAAE,uBAAuB,EAAE,KAAK,oBAAoB,EAAE,MAAM,kBAAkB,CAAC;AAExG,OAAO,EACL,aAAa,EACb,OAAO,EACP,WAAW,EACX,QAAQ,EACR,YAAY,EACZ,YAAY,EACZ,UAAU,EACV,UAAU,EACV,QAAQ,EACR,UAAU,EACV,aAAa,EACb,KAAK,kBAAkB,EACvB,KAAK,WAAW,GACjB,MAAM,WAAW,CAAC;AAEnB,OAAO,EAAE,SAAS,EAAE,KAAK,YAAY,EAAE,KAAK,cAAc,EAAE,MAAM,WAAW,CAAC;AAC9E,OAAO,EAAE,UAAU,EAAE,KAAK,eAAe,EAAE,MAAM,YAAY,CAAC;AAC9D,OAAO,EAAE,cAAc,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAC"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `@connextar/house/setup-guide` — the checklist a new account works through,
|
|
3
|
+
* and what finishing a step earns.
|
|
4
|
+
*
|
|
5
|
+
* It exists because the hard parts are not the checklist. They are: knowing
|
|
6
|
+
* what is done without storing it (so a thing made any other way still counts),
|
|
7
|
+
* paying for it exactly once however many tabs ask at the same moment, and
|
|
8
|
+
* saying so while the person is still looking at what they just did. Those are
|
|
9
|
+
* the same three problems in every app, and they have one right answer each.
|
|
10
|
+
*
|
|
11
|
+
* What the host supplies is everything that is genuinely its own: the steps,
|
|
12
|
+
* how each one is read from its records, where the reward is written, and what
|
|
13
|
+
* a reward is even called. This package holds no routes, makes no requests,
|
|
14
|
+
* imports no toast library and knows nothing about a database.
|
|
15
|
+
*
|
|
16
|
+
* The three surfaces are a card for a home page, a strip for the screen where
|
|
17
|
+
* an item's steps are actually done, and a launcher for the header. The panel
|
|
18
|
+
* the launcher opens is the app's own sheet — see `launcher.tsx`.
|
|
19
|
+
*/
|
|
20
|
+
export { buildGuide, doneSteps, findItem, itemProgress, rewardsOwed, stepKeysOf, stepState, stepsOf, } from "./guide.js";
|
|
21
|
+
export { DEFAULT_REWARD_WORDS, rewardWord, SetupGuideProvider, useSetupGuide, } from "./context.js";
|
|
22
|
+
export { SETUP_BAND_STYLE, SETUP_GUIDE_CLASS_NAMES } from "./class-names.js";
|
|
23
|
+
export { GuideIconLink, HowText, HowToButton, ItemHelp, MiniProgress, ProgressRing, RewardChip, RewardRing, Segments, StepCircle, useClassNames, } from "./bits.js";
|
|
24
|
+
export { SetupCard } from "./card.js";
|
|
25
|
+
export { SetupStrip } from "./strip.js";
|
|
26
|
+
export { SetupGuideList, SetupGuideSummary, SetupLauncherButton } from "./launcher.js";
|
|
27
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/setup-guide/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AACH,OAAO,EACL,UAAU,EACV,SAAS,EACT,QAAQ,EACR,YAAY,EACZ,WAAW,EACX,UAAU,EACV,SAAS,EACT,OAAO,GAYR,MAAM,YAAY,CAAC;AAEpB,OAAO,EACL,oBAAoB,EACpB,UAAU,EACV,kBAAkB,EAClB,aAAa,GAQd,MAAM,cAAc,CAAC;AAEtB,OAAO,EAAE,gBAAgB,EAAE,uBAAuB,EAA6B,MAAM,kBAAkB,CAAC;AAExG,OAAO,EACL,aAAa,EACb,OAAO,EACP,WAAW,EACX,QAAQ,EACR,YAAY,EACZ,YAAY,EACZ,UAAU,EACV,UAAU,EACV,QAAQ,EACR,UAAU,EACV,aAAa,GAGd,MAAM,WAAW,CAAC;AAEnB,OAAO,EAAE,SAAS,EAA0C,MAAM,WAAW,CAAC;AAC9E,OAAO,EAAE,UAAU,EAAwB,MAAM,YAAY,CAAC;AAC9D,OAAO,EAAE,cAAc,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAC"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { type ClassNameOverrides } from "./bits.js";
|
|
2
|
+
/**
|
|
3
|
+
* The header's Get started, and the contents of the panel it opens.
|
|
4
|
+
*
|
|
5
|
+
* The panel itself is the app's own — every one of them has a sheet or a drawer
|
|
6
|
+
* already, with its own idea of how a thing slides in and what closes it. So
|
|
7
|
+
* this package ships what goes *inside* one (`SetupGuideSummary`,
|
|
8
|
+
* `SetupGuideList`) and a button that opens it, and the app puts them together:
|
|
9
|
+
*
|
|
10
|
+
* ```tsx
|
|
11
|
+
* <SetupLauncherButton />
|
|
12
|
+
* <Sheet open={setup.panelOpen} onOpenChange={setup.setPanelOpen}>
|
|
13
|
+
* <SheetContent>
|
|
14
|
+
* <SetupGuideSummary />
|
|
15
|
+
* <SetupGuideList onGo={() => setup.setPanelOpen(false)} />
|
|
16
|
+
* </SheetContent>
|
|
17
|
+
* </Sheet>
|
|
18
|
+
* ```
|
|
19
|
+
*/
|
|
20
|
+
/** The pill: how far along, what has been earned, and a way into the whole guide. */
|
|
21
|
+
export declare function SetupLauncherButton({ label, className, classNames, }: {
|
|
22
|
+
label?: string;
|
|
23
|
+
className?: string;
|
|
24
|
+
classNames?: ClassNameOverrides;
|
|
25
|
+
}): import("react").JSX.Element | null;
|
|
26
|
+
/** The block at the top of the panel: the ring, the count, and one bar per item. */
|
|
27
|
+
export declare function SetupGuideSummary({ className, classNames }: {
|
|
28
|
+
className?: string;
|
|
29
|
+
classNames?: ClassNameOverrides;
|
|
30
|
+
}): import("react").JSX.Element | null;
|
|
31
|
+
/** Every item as a row, for the app's own panel. */
|
|
32
|
+
export declare function SetupGuideList({ onGo, className, classNames, }: {
|
|
33
|
+
/** Called when a row's action is followed, so the panel can close itself. */
|
|
34
|
+
onGo?: () => void;
|
|
35
|
+
className?: string;
|
|
36
|
+
classNames?: ClassNameOverrides;
|
|
37
|
+
}): import("react").JSX.Element | null;
|
|
38
|
+
//# sourceMappingURL=launcher.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"launcher.d.ts","sourceRoot":"","sources":["../../src/setup-guide/launcher.tsx"],"names":[],"mappings":"AAKA,OAAO,EASL,KAAK,kBAAkB,EACxB,MAAM,WAAW,CAAC;AAInB;;;;;;;;;;;;;;;;;GAiBG;AAEH,qFAAqF;AACrF,wBAAgB,mBAAmB,CAAC,EAClC,KAAqB,EACrB,SAAS,EACT,UAAU,GACX,EAAE;IACD,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,kBAAkB,CAAC;CACjC,sCA6BA;AAED,oFAAoF;AACpF,wBAAgB,iBAAiB,CAAC,EAAE,SAAS,EAAE,UAAU,EAAE,EAAE;IAAE,SAAS,CAAC,EAAE,MAAM,CAAC;IAAC,UAAU,CAAC,EAAE,kBAAkB,CAAA;CAAE,sCAoBnH;AAED,oDAAoD;AACpD,wBAAgB,cAAc,CAAC,EAC7B,IAAI,EACJ,SAAS,EACT,UAAU,GACX,EAAE;IACD,6EAA6E;IAC7E,IAAI,CAAC,EAAE,MAAM,IAAI,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,kBAAkB,CAAC;CACjC,sCAmBA"}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
3
|
+
import { ArrowRight, Sparkles } from "lucide-react";
|
|
4
|
+
import { cx } from "../internal/cx.js";
|
|
5
|
+
import { ItemHelp, MiniProgress, ProgressRing, RewardChip, RewardRing, Segments, StepCircle, useClassNames, } from "./bits.js";
|
|
6
|
+
import { rewardWord, useSetupGuide } from "./context.js";
|
|
7
|
+
/**
|
|
8
|
+
* The header's Get started, and the contents of the panel it opens.
|
|
9
|
+
*
|
|
10
|
+
* The panel itself is the app's own — every one of them has a sheet or a drawer
|
|
11
|
+
* already, with its own idea of how a thing slides in and what closes it. So
|
|
12
|
+
* this package ships what goes *inside* one (`SetupGuideSummary`,
|
|
13
|
+
* `SetupGuideList`) and a button that opens it, and the app puts them together:
|
|
14
|
+
*
|
|
15
|
+
* ```tsx
|
|
16
|
+
* <SetupLauncherButton />
|
|
17
|
+
* <Sheet open={setup.panelOpen} onOpenChange={setup.setPanelOpen}>
|
|
18
|
+
* <SheetContent>
|
|
19
|
+
* <SetupGuideSummary />
|
|
20
|
+
* <SetupGuideList onGo={() => setup.setPanelOpen(false)} />
|
|
21
|
+
* </SheetContent>
|
|
22
|
+
* </Sheet>
|
|
23
|
+
* ```
|
|
24
|
+
*/
|
|
25
|
+
/** The pill: how far along, what has been earned, and a way into the whole guide. */
|
|
26
|
+
export function SetupLauncherButton({ label = "Get started", className, classNames, }) {
|
|
27
|
+
const setup = useSetupGuide();
|
|
28
|
+
const c = useClassNames(classNames);
|
|
29
|
+
const guide = setup?.guide;
|
|
30
|
+
if (!setup || !guide || guide.hidden || guide.complete)
|
|
31
|
+
return null;
|
|
32
|
+
const words = setup.words;
|
|
33
|
+
const share = guide.items.length === 0 ? 0 : Math.round((guide.doneItems / guide.items.length) * 100);
|
|
34
|
+
return (_jsxs("button", { type: "button", onClick: () => setup.setPanelOpen(true), className: cx(c.launcher, className), "aria-label": `${label}: ${guide.doneItems} of ${guide.items.length} steps done, ${guide.earnedReward} of ${guide.totalReward} ${rewardWord(guide.totalReward, words)} earned`, children: [_jsx(ProgressRing, { value: share }), _jsx("span", { className: "hidden lg:inline", children: label }), _jsxs("span", { className: "inline-flex h-[22px] items-center gap-1 rounded-full border border-primary/30 bg-primary/10 px-1.5 text-xs text-primary tabular-nums", children: [_jsx(Sparkles, { className: "size-3", "aria-hidden": true }), guide.earnedReward, "/", guide.totalReward] }), setup.justEarned ? (_jsxs("span", { "aria-hidden": true, className: c.launcherBadge, children: ["+", setup.justEarned] })) : null] }));
|
|
35
|
+
}
|
|
36
|
+
/** The block at the top of the panel: the ring, the count, and one bar per item. */
|
|
37
|
+
export function SetupGuideSummary({ className, classNames }) {
|
|
38
|
+
const setup = useSetupGuide();
|
|
39
|
+
const guide = setup?.guide;
|
|
40
|
+
if (!setup || !guide)
|
|
41
|
+
return null;
|
|
42
|
+
const words = setup.words;
|
|
43
|
+
return (_jsxs("div", { className: cx("flex items-center gap-3.5 rounded-xl border border-border bg-muted/40 p-3.5", className), children: [_jsx(RewardRing, { earned: guide.earnedReward, total: guide.totalReward, size: 60, tone: "light", words: words }), _jsxs("div", { className: "flex min-w-0 flex-1 flex-col gap-1.5", children: [_jsxs("p", { className: "text-[15px] font-semibold", children: [guide.earnedReward, " of ", guide.totalReward, " ", rewardWord(guide.totalReward, words), " earned"] }), _jsxs("p", { className: "text-xs text-muted-foreground", children: [guide.doneItems, " of ", guide.items.length, " steps done"] }), _jsx(Segments, { done: guide.doneItems, total: guide.items.length, tone: "light", classNames: classNames })] })] }));
|
|
44
|
+
}
|
|
45
|
+
/** Every item as a row, for the app's own panel. */
|
|
46
|
+
export function SetupGuideList({ onGo, className, classNames, }) {
|
|
47
|
+
const setup = useSetupGuide();
|
|
48
|
+
const guide = setup?.guide;
|
|
49
|
+
if (!setup || !guide)
|
|
50
|
+
return null;
|
|
51
|
+
return (_jsx("ol", { className: className, children: guide.items.map((item, index) => (_jsx(PanelRow, { item: item, index: index, isNext: item.key === guide.nextKey, onGo: onGo, classNames: classNames }, item.key))) }));
|
|
52
|
+
}
|
|
53
|
+
function PanelRow({ item, index, isNext, onGo, classNames, }) {
|
|
54
|
+
const c = useClassNames(classNames);
|
|
55
|
+
const setup = useSetupGuide();
|
|
56
|
+
const Link = setup?.Link ?? (({ children, ...rest }) => _jsx("a", { ...rest, children: children }));
|
|
57
|
+
const paid = item.steps.length > 0 && item.steps.every((step) => step.earned);
|
|
58
|
+
return (_jsxs("li", { className: "flex gap-3 border-b border-border py-3.5 last:border-b-0", children: [_jsx(StepCircle, { state: item.done ? "done" : isNext ? "next" : "todo", n: index + 1, size: "sm", className: "mt-px", classNames: classNames }), _jsxs("div", { className: "flex min-w-0 flex-1 flex-col gap-1.5", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsxs("p", { className: cx("flex-1 text-sm font-semibold", item.done && c.rowTitleDone), children: [item.title, _jsx("span", { className: c.srOnly, children: item.done ? " (done)" : " (to do)" })] }), item.reward > 0 ? (_jsx(RewardChip, { reward: item.reward, state: paid ? "earned" : "todo", classNames: classNames })) : null] }), item.done ? null : (_jsxs(_Fragment, { children: [item.steps.length > 1 ? (_jsxs(_Fragment, { children: [_jsx(MiniProgress, { done: item.doneCount, total: item.steps.length }), isNext && item.screen ? (_jsx("p", { className: "text-xs text-muted-foreground", children: "Its steps show on that screen, with a progress bar." })) : null] })) : null, _jsxs("div", { className: "flex items-center gap-1", children: [item.blocked ? (_jsx("span", { className: c.blocked, children: item.blocked })) : (_jsxs(Link, { href: item.href, onClick: onGo, className: cx(c.button, isNext ? c.buttonPrimary : c.buttonOutline), children: [item.actionLabel, isNext ? _jsx(ArrowRight, { className: "size-4", "aria-hidden": true }) : null] })), _jsx(ItemHelp, { item: item, className: "ml-auto", classNames: classNames })] })] }))] })] }));
|
|
59
|
+
}
|
|
60
|
+
//# sourceMappingURL=launcher.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"launcher.js","sourceRoot":"","sources":["../../src/setup-guide/launcher.tsx"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { type ClassNameOverrides } from "./bits.js";
|
|
3
|
+
export interface SetupStripProps {
|
|
4
|
+
/** Which item's steps this screen is where you do. */
|
|
5
|
+
itemKey: string;
|
|
6
|
+
/**
|
|
7
|
+
* Steps whose choices are made but not yet saved — for a flow that keeps
|
|
8
|
+
* everything until its last press. They fill the bar without being earned.
|
|
9
|
+
*/
|
|
10
|
+
ready?: ReadonlySet<string>;
|
|
11
|
+
className?: string;
|
|
12
|
+
classNames?: ClassNameOverrides;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* One item's steps, on the screen where they are done, with a progress bar.
|
|
16
|
+
*
|
|
17
|
+
* Gone as soon as every step is paid: a screen somebody uses every day should
|
|
18
|
+
* not keep a checklist on it for ever.
|
|
19
|
+
*/
|
|
20
|
+
export declare function SetupStrip({ itemKey, ready, className, classNames }: SetupStripProps): React.JSX.Element | null;
|
|
21
|
+
//# sourceMappingURL=strip.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"strip.d.ts","sourceRoot":"","sources":["../../src/setup-guide/strip.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAG/B,OAAO,EAAqE,KAAK,kBAAkB,EAAE,MAAM,WAAW,CAAC;AAYvH,MAAM,WAAW,eAAe;IAC9B,sDAAsD;IACtD,OAAO,EAAE,MAAM,CAAC;IAChB;;;OAGG;IACH,KAAK,CAAC,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;IAC5B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,kBAAkB,CAAC;CACjC;AAED;;;;;GAKG;AACH,wBAAgB,UAAU,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,UAAU,EAAE,EAAE,eAAe,4BAuFpF"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { ChevronDown } from "lucide-react";
|
|
4
|
+
import * as React from "react";
|
|
5
|
+
import { cx } from "../internal/cx.js";
|
|
6
|
+
import { GuideIconLink, HowToButton, RewardChip, StepCircle, useClassNames } from "./bits.js";
|
|
7
|
+
import { rewardWord, useSetupGuide } from "./context.js";
|
|
8
|
+
import { findItem, itemProgress, stepState } from "./guide.js";
|
|
9
|
+
// Answered by the strip's own width: beside a sidebar, the window says nothing
|
|
10
|
+
// useful about how much room this actually has.
|
|
11
|
+
const COLUMNS = {
|
|
12
|
+
3: "@2xl:grid-cols-3",
|
|
13
|
+
4: "@3xl:grid-cols-4",
|
|
14
|
+
5: "@4xl:grid-cols-5",
|
|
15
|
+
};
|
|
16
|
+
/**
|
|
17
|
+
* One item's steps, on the screen where they are done, with a progress bar.
|
|
18
|
+
*
|
|
19
|
+
* Gone as soon as every step is paid: a screen somebody uses every day should
|
|
20
|
+
* not keep a checklist on it for ever.
|
|
21
|
+
*/
|
|
22
|
+
export function SetupStrip({ itemKey, ready, className, classNames }) {
|
|
23
|
+
const setup = useSetupGuide();
|
|
24
|
+
const c = useClassNames(classNames);
|
|
25
|
+
const [open, setOpen] = React.useState(true);
|
|
26
|
+
const item = findItem(setup?.guide, itemKey);
|
|
27
|
+
if (!setup?.guide || setup.guide.hidden || !item)
|
|
28
|
+
return null;
|
|
29
|
+
if (item.steps.length === 0 || item.steps.every((step) => step.earned))
|
|
30
|
+
return null;
|
|
31
|
+
const words = setup.words;
|
|
32
|
+
const { progressed, total, percent, nextKey } = itemProgress(item, ready);
|
|
33
|
+
const owing = item.reward - item.earnedReward;
|
|
34
|
+
const summary = item.paysOnComplete
|
|
35
|
+
? `${progressed} of ${total} ready · ${owing} ${rewardWord(owing, words)} land when it is built`
|
|
36
|
+
: `${item.doneCount} of ${total} done · ${item.earnedReward} of ${item.reward} ${rewardWord(item.reward, words)} earned`;
|
|
37
|
+
return (_jsxs("section", { "aria-label": item.title, className: cx("@container", c.strip, className), children: [_jsxs("div", { className: "flex flex-wrap items-center gap-x-3.5 gap-y-3", children: [item.icon ? (_jsx("span", { className: "flex size-9 shrink-0 items-center justify-center rounded-xl bg-secondary", children: item.icon })) : null, _jsxs("div", { className: "min-w-0 flex-1", children: [_jsx("h2", { className: c.stripTitle, children: item.title }), _jsx("p", { className: c.stripSummary, children: summary })] }), _jsxs("div", { className: "flex w-full items-center gap-3 sm:w-auto", children: [_jsx("div", { role: "progressbar", "aria-label": `${item.title}: ${percent}%`, "aria-valuemin": 0, "aria-valuemax": 100, "aria-valuenow": percent, className: cx(c.bar, "flex-1 sm:w-56"), children: _jsx("div", { className: c.barFill, style: { width: `${percent}%` } }) }), _jsxs("span", { className: "text-[13px] font-semibold tabular-nums", children: [percent, "%"] }), _jsx("button", { type: "button", "aria-expanded": open, "aria-label": open ? "Hide the steps" : "Show the steps", onClick: () => setOpen((current) => !current), className: c.iconButton, children: _jsx(ChevronDown, { className: cx("size-4 transition-transform", !open && "-rotate-90"), "aria-hidden": true }) })] })] }), open ? (_jsx("ol", { className: cx("mt-3.5 grid gap-2.5 @md:grid-cols-2", COLUMNS[total]), children: item.steps.map((step, index) => {
|
|
38
|
+
const state = stepState(step, nextKey, ready);
|
|
39
|
+
return (_jsxs("li", { className: cx(c.stripTile, state === "next" && c.stripTileNext), children: [_jsxs("div", { className: "flex items-center justify-between gap-2", children: [_jsx(StepCircle, { state: state, n: index + 1, size: "sm", classNames: classNames }), step.reward > 0 ? (_jsx(RewardChip, { reward: step.reward, state: step.earned ? "earned" : item.paysOnComplete ? "on-complete" : "todo", words: words, classNames: classNames })) : null] }), _jsxs("p", { className: cx("text-[13.5px] leading-snug font-semibold", step.done && c.rowTitleDone), children: [step.title, _jsx("span", { className: c.srOnly, children: step.done ? " (done)" : state === "ready" ? " (ready — pays when built)" : " (to do)" })] }), _jsxs("div", { className: "-ml-1.5 mt-auto flex items-center", children: [step.how ? (_jsx(HowToButton, { how: step.how, guide: step.guide, className: "size-7", classNames: classNames })) : null, _jsx(GuideIconLink, { guide: step.guide, className: "size-7", classNames: classNames }), state === "next" ? _jsx("span", { className: "ml-auto text-xs font-semibold text-primary", children: "Next" }) : null] })] }, step.key));
|
|
40
|
+
}) })) : null] }));
|
|
41
|
+
}
|
|
42
|
+
//# sourceMappingURL=strip.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"strip.js","sourceRoot":"","sources":["../../src/setup-guide/strip.tsx"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `@connextar/house/wizard` — one long form, split into steps that check
|
|
3
|
+
* themselves.
|
|
4
|
+
*
|
|
5
|
+
* Five apps wrote this and four of them shipped a defect in it: a booking
|
|
6
|
+
* created from step two because two buttons shared a slot and React switched the
|
|
7
|
+
* type mid-click; three forms that refused to save with nothing on screen saying
|
|
8
|
+
* why; a stepper that checked no step at all on submit and sent a record whose
|
|
9
|
+
* first step had been cleared. None of those are mistakes about wizards. They are
|
|
10
|
+
* mistakes about what a form must do before it agrees to send, and there is one
|
|
11
|
+
* right answer to that, which is why this is here rather than in five places.
|
|
12
|
+
*
|
|
13
|
+
* Form-library-agnostic on purpose: the host supplies the check
|
|
14
|
+
* (`onValidateStep`, or a `validate` on the step), so react-hook-form, plain
|
|
15
|
+
* state and a form held in `useState` can all host it. The react-hook-form
|
|
16
|
+
* adapter is `@connextar/house/wizard/react-hook-form`, kept apart so that
|
|
17
|
+
* `react-hook-form` stays the optional peer dependency it is declared to be.
|
|
18
|
+
*
|
|
19
|
+
* What reaches the person, when a refusal has nowhere to show itself, goes
|
|
20
|
+
* through the `ActionFeedbackProvider` from `@connextar/house/ux` — so this
|
|
21
|
+
* module imports no toast library and no design system.
|
|
22
|
+
*/
|
|
23
|
+
export { ERROR_ATTRIBUTE, fieldProblems, INVALID_MESSAGES, isOnScreen, REPORT_DELAY, reportInvalid, shownErrorFields, unseenProblems, useInvalidReporter, type FieldProblem, type InvalidMessages, type InvalidReporter, } from "./invalid.js";
|
|
24
|
+
export { checkStep, firstFailure, isReachable, markerState, nextIndex, percentComplete, previousIndex, stepResult, type MarkerState, type StepCheck, type StepCheckable, type StepFailure, type StepOutcome, type StepResult, } from "./rules.js";
|
|
25
|
+
export { FieldError, FormWizard, ReviewRow, WIZARD_CLASS_NAMES, type FormWizardProps, type WizardClassNames, type WizardStep, } from "./wizard.js";
|
|
26
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/wizard/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,OAAO,EACL,eAAe,EACf,aAAa,EACb,gBAAgB,EAChB,UAAU,EACV,YAAY,EACZ,aAAa,EACb,gBAAgB,EAChB,cAAc,EACd,kBAAkB,EAClB,KAAK,YAAY,EACjB,KAAK,eAAe,EACpB,KAAK,eAAe,GACrB,MAAM,cAAc,CAAC;AACtB,OAAO,EACL,SAAS,EACT,YAAY,EACZ,WAAW,EACX,WAAW,EACX,SAAS,EACT,eAAe,EACf,aAAa,EACb,UAAU,EACV,KAAK,WAAW,EAChB,KAAK,SAAS,EACd,KAAK,aAAa,EAClB,KAAK,WAAW,EAChB,KAAK,WAAW,EAChB,KAAK,UAAU,GAChB,MAAM,YAAY,CAAC;AACpB,OAAO,EACL,UAAU,EACV,UAAU,EACV,SAAS,EACT,kBAAkB,EAClB,KAAK,eAAe,EACpB,KAAK,gBAAgB,EACrB,KAAK,UAAU,GAChB,MAAM,aAAa,CAAC"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `@connextar/house/wizard` — one long form, split into steps that check
|
|
3
|
+
* themselves.
|
|
4
|
+
*
|
|
5
|
+
* Five apps wrote this and four of them shipped a defect in it: a booking
|
|
6
|
+
* created from step two because two buttons shared a slot and React switched the
|
|
7
|
+
* type mid-click; three forms that refused to save with nothing on screen saying
|
|
8
|
+
* why; a stepper that checked no step at all on submit and sent a record whose
|
|
9
|
+
* first step had been cleared. None of those are mistakes about wizards. They are
|
|
10
|
+
* mistakes about what a form must do before it agrees to send, and there is one
|
|
11
|
+
* right answer to that, which is why this is here rather than in five places.
|
|
12
|
+
*
|
|
13
|
+
* Form-library-agnostic on purpose: the host supplies the check
|
|
14
|
+
* (`onValidateStep`, or a `validate` on the step), so react-hook-form, plain
|
|
15
|
+
* state and a form held in `useState` can all host it. The react-hook-form
|
|
16
|
+
* adapter is `@connextar/house/wizard/react-hook-form`, kept apart so that
|
|
17
|
+
* `react-hook-form` stays the optional peer dependency it is declared to be.
|
|
18
|
+
*
|
|
19
|
+
* What reaches the person, when a refusal has nowhere to show itself, goes
|
|
20
|
+
* through the `ActionFeedbackProvider` from `@connextar/house/ux` — so this
|
|
21
|
+
* module imports no toast library and no design system.
|
|
22
|
+
*/
|
|
23
|
+
export { ERROR_ATTRIBUTE, fieldProblems, INVALID_MESSAGES, isOnScreen, REPORT_DELAY, reportInvalid, shownErrorFields, unseenProblems, useInvalidReporter, } from "./invalid.js";
|
|
24
|
+
export { checkStep, firstFailure, isReachable, markerState, nextIndex, percentComplete, previousIndex, stepResult, } from "./rules.js";
|
|
25
|
+
export { FieldError, FormWizard, ReviewRow, WIZARD_CLASS_NAMES, } from "./wizard.js";
|
|
26
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/wizard/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,OAAO,EACL,eAAe,EACf,aAAa,EACb,gBAAgB,EAChB,UAAU,EACV,YAAY,EACZ,aAAa,EACb,gBAAgB,EAChB,cAAc,EACd,kBAAkB,GAInB,MAAM,cAAc,CAAC;AACtB,OAAO,EACL,SAAS,EACT,YAAY,EACZ,WAAW,EACX,WAAW,EACX,SAAS,EACT,eAAe,EACf,aAAa,EACb,UAAU,GAOX,MAAM,YAAY,CAAC;AACpB,OAAO,EACL,UAAU,EACV,UAAU,EACV,SAAS,EACT,kBAAkB,GAInB,MAAM,aAAa,CAAC"}
|
|
@@ -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"}
|