@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.
Files changed (58) hide show
  1. package/README.md +242 -6
  2. package/dist/setup-guide/bits.d.ts +89 -0
  3. package/dist/setup-guide/bits.d.ts.map +1 -0
  4. package/dist/setup-guide/bits.js +134 -0
  5. package/dist/setup-guide/bits.js.map +1 -0
  6. package/dist/setup-guide/card.d.ts +33 -0
  7. package/dist/setup-guide/card.d.ts.map +1 -0
  8. package/dist/setup-guide/card.js +56 -0
  9. package/dist/setup-guide/card.js.map +1 -0
  10. package/dist/setup-guide/class-names.d.ts +68 -0
  11. package/dist/setup-guide/class-names.d.ts.map +1 -0
  12. package/dist/setup-guide/class-names.js +63 -0
  13. package/dist/setup-guide/class-names.js.map +1 -0
  14. package/dist/setup-guide/context.d.ts +89 -0
  15. package/dist/setup-guide/context.d.ts.map +1 -0
  16. package/dist/setup-guide/context.js +124 -0
  17. package/dist/setup-guide/context.js.map +1 -0
  18. package/dist/setup-guide/guide.d.ts +159 -0
  19. package/dist/setup-guide/guide.d.ts.map +1 -0
  20. package/dist/setup-guide/guide.js +128 -0
  21. package/dist/setup-guide/guide.js.map +1 -0
  22. package/dist/setup-guide/index.d.ts +27 -0
  23. package/dist/setup-guide/index.d.ts.map +1 -0
  24. package/dist/setup-guide/index.js +27 -0
  25. package/dist/setup-guide/index.js.map +1 -0
  26. package/dist/setup-guide/launcher.d.ts +38 -0
  27. package/dist/setup-guide/launcher.d.ts.map +1 -0
  28. package/dist/setup-guide/launcher.js +60 -0
  29. package/dist/setup-guide/launcher.js.map +1 -0
  30. package/dist/setup-guide/strip.d.ts +21 -0
  31. package/dist/setup-guide/strip.d.ts.map +1 -0
  32. package/dist/setup-guide/strip.js +42 -0
  33. package/dist/setup-guide/strip.js.map +1 -0
  34. package/dist/wizard/index.d.ts +26 -0
  35. package/dist/wizard/index.d.ts.map +1 -0
  36. package/dist/wizard/index.js +26 -0
  37. package/dist/wizard/index.js.map +1 -0
  38. package/dist/wizard/invalid.d.ts +91 -0
  39. package/dist/wizard/invalid.d.ts.map +1 -0
  40. package/dist/wizard/invalid.js +121 -0
  41. package/dist/wizard/invalid.js.map +1 -0
  42. package/dist/wizard/react-hook-form-index.d.ts +12 -0
  43. package/dist/wizard/react-hook-form-index.d.ts.map +1 -0
  44. package/dist/wizard/react-hook-form-index.js +12 -0
  45. package/dist/wizard/react-hook-form-index.js.map +1 -0
  46. package/dist/wizard/rhf-adapter.d.ts +32 -0
  47. package/dist/wizard/rhf-adapter.d.ts.map +1 -0
  48. package/dist/wizard/rhf-adapter.js +58 -0
  49. package/dist/wizard/rhf-adapter.js.map +1 -0
  50. package/dist/wizard/rules.d.ts +92 -0
  51. package/dist/wizard/rules.d.ts.map +1 -0
  52. package/dist/wizard/rules.js +105 -0
  53. package/dist/wizard/rules.js.map +1 -0
  54. package/dist/wizard/wizard.d.ts +127 -0
  55. package/dist/wizard/wizard.d.ts.map +1 -0
  56. package/dist/wizard/wizard.js +195 -0
  57. package/dist/wizard/wizard.js.map +1 -0
  58. package/package.json +19 -2
package/README.md CHANGED
@@ -47,6 +47,19 @@ it. It is also the kind of code nobody should write twice: the rules about when
47
47
  to let go of an optimistic change are small, subtle and easy to get quietly
48
48
  wrong.
49
49
 
50
+ `wizard`, in 0.4.0, is the first module extracted from five copies that had
51
+ already been written. It is here because of what those copies cost: four of the
52
+ five shipped a defect in it, and not one of the four was a mistake about
53
+ wizards. PharmaLine created a booking from step two, because a `type="button"`
54
+ Continue and a `type="submit"` Create shared a slot and React switched the one
55
+ DOM node's type mid-click. Three PM Tool forms refused to save with nothing on
56
+ screen saying why. MAS checked no step at all on submit, so a second press after
57
+ a successful create sent a record whose first step had been cleared. Every one of
58
+ those is a mistake about what a form must do before it agrees to send, and there
59
+ is one right answer to that — which is exactly the shape of thing that belongs
60
+ here and nowhere else. The module is the union of the five, not a copy of the
61
+ best one.
62
+
50
63
  ## Modules
51
64
 
52
65
  ### `@connextar/house/errors`
@@ -305,6 +318,179 @@ learned the hard way:
305
318
  exports: anything whose result the person cannot predict, or would be alarmed to
306
319
  see undone. Guessing is only honest when the guess is nearly always right.
307
320
 
321
+ ### `@connextar/house/wizard`, `/wizard/react-hook-form`
322
+
323
+ `FormWizard` — one long form split into steps that check themselves. **It renders
324
+ the `<form>`**, because two of its promises cannot be kept from inside somebody
325
+ else's: that Enter has one place to land, and that nothing reaches `onSubmit`
326
+ unchecked.
327
+
328
+ ```tsx
329
+ const check = useStepValidator(form); // react-hook-form; or write your own
330
+
331
+ <FormWizard
332
+ steps={[
333
+ { id: "learner", title: "Learner", fields: ["studentId", "level"], content: <LearnerFields /> },
334
+ { id: "period", title: "Period", fields: ["startDate", "endDate"], content: <PeriodFields /> },
335
+ { id: "build", title: "Build", fields: [], content: <ReviewFields /> },
336
+ ]}
337
+ onValidateStep={check}
338
+ onSubmit={() => void form.handleSubmit(submit, report)()}
339
+ onCancel={() => router.push(routes.plans)}
340
+ submitLabel="Build my curriculum"
341
+ pending={pending}
342
+ />;
343
+ ```
344
+
345
+ The rules it keeps, each one because a copy of it did not:
346
+
347
+ - **Submitting checks every step, in order, and lands on the first that fails**,
348
+ with its message visible. Checking only the last step lets a marker jump back,
349
+ a cleared field and a press of Save refuse with the problem three screens away.
350
+ Checking none — MAS — sends the record anyway.
351
+ - **Enter before the last step advances.** It validates the step it is on and
352
+ opens the next; only the last step's Enter submits. A booking that emails a
353
+ patient must not be creatable from the step before Confirm.
354
+ - **One submit button, whose `type` never changes.** Never two controls sharing
355
+ the slot. jsdom's timing hid this, and only Chromium showed it, so the test
356
+ that guards it is structural: one `button[type=submit]`, the same node on every
357
+ step.
358
+ - **Continue is never disabled for being incomplete.** A greyed-out button that
359
+ will not say what is missing is a refusal with no words. It is disabled only
360
+ while something is in flight.
361
+ - **Visited markers are reachable, later steps locked** (`allReachable` opens
362
+ them all, for editing values that are already there).
363
+ - **Every step stays mounted, hidden rather than unmounted**, so there is one
364
+ field register and nothing typed is lost stepping back.
365
+
366
+ **Form-library-agnostic, because the five hosts are not the same.** A step is
367
+ complete when the check says so, and a check may answer either way the cohort
368
+ already writes them: `true`/`null` for complete, `false` for "not complete, and I
369
+ am showing why beside the field", or **a sentence** for "not complete, and this is
370
+ what to say" — which the wizard then shows in a `role="alert"` of its own. So
371
+ react-hook-form, plain `useState` and MAS's state forms all host it unchanged. A
372
+ check may live on the form (`onValidateStep`) or on the step (`step.validate`);
373
+ both are asked, the step's first.
374
+
375
+ `stepValidator(form, report)` and `useStepValidator(form)` are in
376
+ `@connextar/house/wizard/react-hook-form` — its own entry point because
377
+ `react-hook-form` is an **optional** peer dependency. Every import of it there is
378
+ `import type`, so the emitted JavaScript never mentions it; what would break an
379
+ app without it is a declaration file naming a package it has not installed, which
380
+ is why the names sit behind a subpath MAS never reaches for.
381
+ `src/wizard/entry-points.test.ts` keeps that honest against the built output.
382
+
383
+ **When the error is somewhere nobody can look.** A wizard's fields live on steps
384
+ that are mounted but hidden, and a form library will not call a submit handler
385
+ while any field is invalid — so a message beside a field the person cannot see
386
+ makes the button look broken. `useInvalidReporter()` returns the function to pass
387
+ as the invalid handler (`form.handleSubmit(onSubmit, report)`); it waits for the
388
+ form to re-render and the wizard to move, then says the message out loud **only
389
+ if it is not already on screen**. It goes to the `ActionFeedbackProvider` from
390
+ [`ux`](#connextarhouseux), so this module imports no toast library. A field's
391
+ message opts in by carrying `data-error-for="<name>"` — `FieldError` does it, and
392
+ an app's own message component adds one attribute.
393
+
394
+ The look is theme-token classes, replaced part by part through `classNames`, the
395
+ same way the guide renderer allows — there is still no `Button` in this package,
396
+ so the wizard draws plain elements and wears the app's classes. The busy state is
397
+ `data-busy`, which [`house.css`](#styles--connextarhousehousecss) already turns
398
+ into a spinner.
399
+
400
+ **What not to make a wizard.** A form of four fields is shorter than a wizard
401
+ around it, and a stepper over one screen of content is decoration that costs a
402
+ click. This is for a form long enough that a person would otherwise lose their
403
+ place — five steps in the curriculum builder, a booking that has to reach Confirm
404
+ — and specifically for one where **later steps depend on earlier answers**. Three
405
+ independent sections are a form with three headings. A flow whose steps are
406
+ separate saves, each landing before the next opens, is not one form and must not
407
+ pretend to be: this wizard keeps one register and submits once.
408
+
409
+ ### `@connextar/house/setup-guide`
410
+
411
+ The checklist a new account works through, and what finishing a step earns.
412
+
413
+ The checklist is not the hard part. The three things underneath it are, and each
414
+ has one right answer:
415
+
416
+ - **What is done is never stored.** A step is done when the account _holds_ what
417
+ it asks for — a learner, a price, a published course — read fresh every time.
418
+ So a thing made from the grid, from an import, or by an administrator counts
419
+ exactly as much as one made from the guide, and there is no status column to
420
+ drift away from the truth.
421
+ - **A reward is paid once.** That is the only thing written down: one entry per
422
+ step per account, keyed on the step, refused by a unique index the second
423
+ time. Two tabs asking at the same moment pay one reward between them.
424
+ - **It is said while the person is still looking at what earned it.** Most saves
425
+ never refresh a page, so the guide listens for the app's own write event,
426
+ waits for a burst of saves to settle, and asks once.
427
+
428
+ ```tsx
429
+ const ITEMS: SetupItemDef[] = [
430
+ {
431
+ key: "learner",
432
+ title: "Add your first learner",
433
+ description: "Every timetable, task and plan hangs off a learner.",
434
+ href: "/dashboard?add=learner",
435
+ actionLabel: "Add your first learner",
436
+ steps: [
437
+ {
438
+ key: "learner.add", // permanent: the reward is keyed on it
439
+ title: "Add your first learner",
440
+ reward: 5,
441
+ how: { title: "How to add a learner", steps: ["Select **Add learner**.", "Type their _name_."] },
442
+ guide: { href: "/guides/learners#add-a-learner", title: "Learners" },
443
+ },
444
+ ],
445
+ },
446
+ ];
447
+
448
+ <SetupGuideProvider
449
+ initial={guideFromTheServer}
450
+ transport={{ sync, setHidden }} // your API calls; this package makes none
451
+ words={{ one: "AI credit", many: "AI credits" }}
452
+ changeEvent="myapp:data-changed"
453
+ link={NextLink}
454
+ >
455
+ <SetupCard variant="full" title="Let's get you set up" lead="A few things to do." />
456
+ </SetupGuideProvider>;
457
+ ```
458
+
459
+ **Three surfaces.** `SetupCard` leads a home page (`full`) or sits as one band
460
+ above it once there is real work to show (`compact`). `SetupStrip` goes on the
461
+ screen where an item's steps are actually done, with a progress bar, and
462
+ disappears the moment they are all paid — a screen somebody uses every day
463
+ should not keep a checklist on it for ever. `SetupLauncherButton` is the header
464
+ pill. Each step carries an ⓘ with how to do it and a link to its guide.
465
+
466
+ **The panel is yours.** Every app already has a sheet, with its own idea of how
467
+ a thing slides in and what closes it, so this ships what goes _inside_ one:
468
+
469
+ ```tsx
470
+ <SetupLauncherButton />
471
+ <Sheet open={setup.panelOpen} onOpenChange={setup.setPanelOpen}>
472
+ <SheetContent>
473
+ <SetupGuideSummary />
474
+ <SetupGuideList onGo={() => setup.setPanelOpen(false)} />
475
+ </SheetContent>
476
+ </Sheet>
477
+ ```
478
+
479
+ **What the host supplies** is everything genuinely its own: the step
480
+ definitions, how each is read from its records, where the reward is written, and
481
+ what a reward is even called. `rewardsOwed(items, done, earned)` is the pure
482
+ half of paying — the host loops over it and writes one row each, letting its own
483
+ unique index refuse the second. `doneSteps(facts, earned)` is the other rule
484
+ worth not rewriting: a reward, once earned, is not taken back, so a step whose
485
+ thing has since been deleted stays ticked rather than reopening and asking to be
486
+ paid again.
487
+
488
+ An app that gives nothing at all leaves `reward` off every step and gets a
489
+ checklist that still works. `src/setup-guide/entry-points.test.ts` holds the
490
+ module to React and its icons — no router, no toast library, no design system —
491
+ because the moment it reaches for one of those, the claim that six apps can host
492
+ it stops being true.
493
+
308
494
  ### Styles — `@connextar/house/house.css`
309
495
 
310
496
  What the components need that utility classes cannot say: rich text, the editor's
@@ -335,9 +521,19 @@ added.
335
521
 
336
522
  (The `@source` path is relative to the stylesheet.)
337
523
 
338
- **Tailwind v3** — add `"./node_modules/@connextar/house/dist/**/*.js"` to
339
- `content` in `tailwind.config`, and import `@connextar/house/house.css` at the top
340
- of the global stylesheet, before the `@tailwind` directives.
524
+ **Tailwind v3** — three pieces, and two of them were only found by adopting it:
525
+
526
+ 1. Add `"./node_modules/@connextar/house/dist/**/*.js"` to `content` in
527
+ `tailwind.config`.
528
+ 2. Import the file by its real path, `@connextar/house/dist/house.css`, at the top
529
+ of the global stylesheet, before the `@tailwind` directives. **Not** the
530
+ `@connextar/house/house.css` export the v4 setup uses: `postcss-import`
531
+ resolves a bare specifier with the classic `node_modules` algorithm, which
532
+ predates `exports` maps, so the export name fails the build outright.
533
+ 3. Run `postcss-import` ahead of Tailwind in `postcss.config`, so the import is
534
+ inlined into the app's own stylesheet. Left to the bundler, `house.css`
535
+ arrives as its own PostCSS entry, where its `@layer components` block has no
536
+ matching `@tailwind components` directive and the build fails.
341
537
 
342
538
  `house.css` reads the app's theme tokens (`--foreground`, `--muted-foreground`,
343
539
  `--primary`, `--muted`, `--border`, `--card`, `--accent`) through a `--house-*`
@@ -407,11 +603,51 @@ Traps found adopting it the first time:
407
603
  module"; delete `.next/dev` when no dev server is running.
408
604
  - If the app also depends on `@tiptap/*` directly, keep the range aligned or drop
409
605
  the direct dependency — two copies of `@tiptap/pm` break the editor.
606
+ - **`FormWizard` renders the `<form>`, so the app's wrapper one has to go.** Every
607
+ local copy but two sat inside a host `<form onSubmit={preventDefault}>`; nested
608
+ forms are invalid HTML and the outer one is where the Enter bug lives. Keep the
609
+ app's `<Form {...form}>` provider if it has one — that is context, not an
610
+ element — and delete the `<form>` inside it. MAS also loses its
611
+ `finishControl`: the submit button is the wizard's now, and passing one back in
612
+ is the defect the wizard exists to prevent.
410
613
  - **Jest** (with `next/jest`) does not transform `node_modules`, and this package is
411
614
  ESM, so the first import fails with `Unexpected token 'export'`. `next/jest` only
412
615
  appends to `transformIgnorePatterns`, so export an async config that rewrites each
413
- `/node_modules/(?!` rule to `/node_modules/(?!@connextar/house/)(?!`. Vitest needs
414
- nothing.
616
+ `/node_modules/(?!` rule to `/node_modules/(?!@connextar/house/)(?!`.
617
+ - **Vitest needs one line to mock anything the package imports.** It loads
618
+ `node_modules` through native Node ESM, where `vi.mock` does not reach: a test
619
+ that mocks `next/navigation` keeps passing while the package's components call
620
+ the real router, until one of them throws `invariant expected app router to be
621
+ mounted`. Inline the package so vite transforms it:
622
+
623
+ ```ts
624
+ test: {
625
+ server: {
626
+ deps: {
627
+ inline: [/@connextar\/house/];
628
+ }
629
+ }
630
+ }
631
+ ```
632
+
633
+ - **Mock `next/navigation.js`, not just `next/navigation`.** This package is built
634
+ with `moduleResolution: nodenext`, and `next` ships no `exports` map — so an
635
+ extensionless subpath does not resolve and the source has to say
636
+ `next/navigation.js`. Vitest keys mocks by specifier, so an app's existing mock
637
+ of `next/navigation` does not cover the package. Register both, with the factory
638
+ written out twice: `vi.mock` is hoisted above every `const`, so a shared factory
639
+ variable throws `Cannot access ... before initialization`.
640
+ - **Both factories must hand back the same spies.** The two specifiers resolve to
641
+ one module, so the second registration wins. A fresh `vi.fn()` in the second
642
+ factory silently replaces the first, and an assertion on `router.refresh` then
643
+ watches a spy nothing ever called — a test that passes while proving nothing.
644
+ Build the spies once at module scope and return those from both factories.
645
+ - **A locale-aware app inlines its i18n package too.** `next-intl`'s
646
+ `createNavigation` also imports `next/navigation` extensionlessly, so with only
647
+ the package inlined the whole suite fails to load: `inline: [/@connextar\/house/, /next-intl/]`.
648
+ For the same reason an app whose router is locale-aware keeps its own
649
+ `useAppRouter` over `next-intl`'s router — swapping in the package's would drop
650
+ the locale prefix from every push, silently.
415
651
 
416
652
  **A note on local development.** Installing this with `npm install ../path` makes
417
653
  a symlink, and Next's Turbopack will not resolve a package whose real path is
@@ -421,7 +657,7 @@ behaves exactly like a registry install:
421
657
 
422
658
  ```bash
423
659
  npm pack --pack-destination /tmp # in this repo
424
- npm install /tmp/connextar-house-0.2.0.tgz # in the app
660
+ npm install /tmp/connextar-house-0.4.0.tgz # in the app
425
661
  ```
426
662
 
427
663
  ## Adding to it
@@ -0,0 +1,89 @@
1
+ import * as React from "react";
2
+ import { type SetupGuideClassNames } from "./class-names.js";
3
+ import { type RewardWords, type SetupLinkComponent } from "./context.js";
4
+ import type { GuideItem, SetupGuideLink, SetupHowTo, StepState } from "./guide.js";
5
+ /** The small parts every drawing of the guide shares. */
6
+ export type ClassNameOverrides = Partial<SetupGuideClassNames>;
7
+ export declare function useClassNames(overrides?: ClassNameOverrides): SetupGuideClassNames;
8
+ /** The words and the link component, with sane answers outside a provider so a part can be rendered alone. */
9
+ export declare function useGuideChrome(): {
10
+ words: RewardWords;
11
+ Link: SetupLinkComponent;
12
+ };
13
+ export type RewardState = "todo" | "earned" | "on-complete";
14
+ /** What a step pays. */
15
+ export declare function RewardChip({ reward, state, tone, words, className, classNames, }: {
16
+ reward: number;
17
+ state?: RewardState;
18
+ tone?: "light" | "dark";
19
+ words?: RewardWords;
20
+ className?: string;
21
+ classNames?: ClassNameOverrides;
22
+ }): React.JSX.Element;
23
+ export declare function StepCircle({ state, n, size, className, classNames, }: {
24
+ state: StepState;
25
+ n?: number;
26
+ size?: "sm" | "md";
27
+ className?: string;
28
+ classNames?: ClassNameOverrides;
29
+ }): React.JSX.Element;
30
+ /** The share earned, as a ring with the number inside. */
31
+ export declare function RewardRing({ earned, total, size, tone, words, }: {
32
+ earned: number;
33
+ total: number;
34
+ size?: number;
35
+ tone?: "dark" | "light";
36
+ words?: RewardWords;
37
+ }): React.JSX.Element;
38
+ /** A small progress ring, for a header. */
39
+ export declare function ProgressRing({ value, size }: {
40
+ value: number;
41
+ size?: number;
42
+ }): React.JSX.Element;
43
+ /** One bar per item, lit when it is done. */
44
+ export declare function Segments({ done, total, tone, classNames, }: {
45
+ done: number;
46
+ total: number;
47
+ tone?: "dark" | "light";
48
+ classNames?: ClassNameOverrides;
49
+ }): React.JSX.Element;
50
+ export declare function MiniProgress({ done, total, className }: {
51
+ done: number;
52
+ total: number;
53
+ className?: string;
54
+ }): React.JSX.Element;
55
+ /** `**label**` in bold and `_example_` in italics — the only markup the instructions use. */
56
+ export declare function HowText({ text }: {
57
+ text: string;
58
+ }): React.JSX.Element;
59
+ /**
60
+ * The ⓘ: how to do this step, beside the step itself.
61
+ *
62
+ * Built here rather than on a popover library, because this package cannot pick
63
+ * one for six apps. It is a disclosure, not a dialog: Escape closes it, a click
64
+ * outside closes it, and focus is left where the person put it — nothing here
65
+ * is worth trapping focus for.
66
+ */
67
+ export declare function HowToButton({ how, guide, footer, className, classNames, }: {
68
+ how: SetupHowTo;
69
+ guide?: SetupGuideLink;
70
+ footer?: React.ReactNode;
71
+ className?: string;
72
+ classNames?: ClassNameOverrides;
73
+ }): React.JSX.Element;
74
+ /** The book: the guide for the feature, in a new tab. */
75
+ export declare function GuideIconLink({ guide, className, classNames, }: {
76
+ guide?: SetupGuideLink;
77
+ className?: string;
78
+ classNames?: ClassNameOverrides;
79
+ }): React.JSX.Element | null;
80
+ /**
81
+ * Both icons for one step — or, for an item with several, its steps listed in
82
+ * the ⓘ and the first step's guide on the book.
83
+ */
84
+ export declare function ItemHelp({ item, className, classNames, }: {
85
+ item: GuideItem;
86
+ className?: string;
87
+ classNames?: ClassNameOverrides;
88
+ }): React.JSX.Element | null;
89
+ //# sourceMappingURL=bits.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bits.d.ts","sourceRoot":"","sources":["../../src/setup-guide/bits.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,134 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
3
+ import { BookOpen, Check, Info, Sparkles } from "lucide-react";
4
+ import * as React from "react";
5
+ import { cx } from "../internal/cx.js";
6
+ import { SETUP_GUIDE_CLASS_NAMES } from "./class-names.js";
7
+ import { rewardWord, useSetupGuide } from "./context.js";
8
+ export function useClassNames(overrides) {
9
+ return React.useMemo(() => ({ ...SETUP_GUIDE_CLASS_NAMES, ...overrides }), [overrides]);
10
+ }
11
+ /** The words and the link component, with sane answers outside a provider so a part can be rendered alone. */
12
+ export function useGuideChrome() {
13
+ const setup = useSetupGuide();
14
+ const fallback = React.useCallback(({ children, ...rest }) => _jsx("a", { ...rest, children: children }), []);
15
+ return { words: setup?.words ?? { one: "credit", many: "credits" }, Link: setup?.Link ?? fallback };
16
+ }
17
+ /** What a step pays. */
18
+ export function RewardChip({ reward, state = "todo", tone = "light", words, className, classNames, }) {
19
+ const c = useClassNames(classNames);
20
+ const chrome = useGuideChrome();
21
+ const said = words ?? chrome.words;
22
+ const unit = rewardWord(reward, said);
23
+ if (state === "earned") {
24
+ return (_jsxs("span", { className: cx(c.chipEarned, tone === "dark" && "text-white", className), children: [_jsx(Check, { className: "size-3.5", "aria-hidden": true }), reward, " earned", _jsxs("span", { className: c.srOnly, children: [" ", unit] })] }));
25
+ }
26
+ return (_jsxs("span", { className: cx(c.chip, tone === "dark" && c.chipDark, state === "on-complete" && tone !== "dark" && c.chipOnComplete, className), children: [_jsx(Sparkles, { className: "size-3", "aria-hidden": true }), "+", reward, state === "on-complete" ? " on build" : null, _jsxs("span", { className: c.srOnly, children: [" ", unit] })] }));
27
+ }
28
+ export function StepCircle({ state, n, size = "md", className, classNames, }) {
29
+ const c = useClassNames(classNames);
30
+ return (_jsx("span", { "aria-hidden": true, className: cx(c.circle, size === "sm" ? "size-6" : "size-7", state === "done" && c.circleDone, state === "next" && c.circleNext, state === "ready" && c.circleReady, state === "todo" && c.circleTodo, className), children: state === "done" || state === "ready" ? _jsx(Check, { className: size === "sm" ? "size-3.5" : "size-4" }) : n }));
31
+ }
32
+ /** The share earned, as a ring with the number inside. */
33
+ export function RewardRing({ earned, total, size = 84, tone = "dark", words, }) {
34
+ const chrome = useGuideChrome();
35
+ const said = words ?? chrome.words;
36
+ const share = total === 0 ? 0 : Math.round((earned / total) * 100);
37
+ return (_jsx("span", { role: "img", "aria-label": `${earned} of ${total} ${rewardWord(total, said)} earned`, className: "flex shrink-0 items-center justify-center rounded-full", style: {
38
+ width: size,
39
+ height: size,
40
+ background: tone === "dark"
41
+ ? `conic-gradient(#ffffff 0 ${share}%, rgb(255 255 255 / 0.12) 0)`
42
+ : `conic-gradient(var(--primary) 0 ${share}%, color-mix(in oklab, var(--primary) 14%, transparent) 0)`,
43
+ }, children: _jsxs("span", { className: cx("flex flex-col items-center justify-center rounded-full leading-none", tone === "dark" ? "bg-[#0e2f27] text-white" : "bg-card text-foreground"), style: { width: size - 14, height: size - 14 }, children: [_jsx("span", { className: "font-bold", style: { fontSize: Math.round(size * 0.34) }, children: earned }), _jsxs("span", { className: cx("mt-1 text-[11px]", tone === "dark" ? "text-slate-400" : "text-muted-foreground"), children: ["of ", total] })] }) }));
44
+ }
45
+ /** A small progress ring, for a header. */
46
+ export function ProgressRing({ value, size = 24 }) {
47
+ return (_jsx("span", { "aria-hidden": true, className: "flex shrink-0 items-center justify-center rounded-full", style: {
48
+ width: size,
49
+ height: size,
50
+ background: `conic-gradient(var(--primary) 0 ${value}%, var(--secondary) 0)`,
51
+ }, children: _jsx("span", { className: "block rounded-full bg-background", style: { width: size - 8, height: size - 8 } }) }));
52
+ }
53
+ /** One bar per item, lit when it is done. */
54
+ export function Segments({ done, total, tone = "dark", classNames, }) {
55
+ const c = useClassNames(classNames);
56
+ return (_jsx("div", { className: "flex gap-1.5", "aria-hidden": true, children: Array.from({ length: total }, (_, index) => (_jsx("span", { className: cx(c.segment, index < done
57
+ ? tone === "dark"
58
+ ? c.segmentDoneDark
59
+ : c.segmentDone
60
+ : tone === "dark"
61
+ ? c.segmentTodoDark
62
+ : c.segmentTodo) }, index))) }));
63
+ }
64
+ export function MiniProgress({ done, total, className }) {
65
+ return (_jsxs("span", { className: cx("inline-flex items-center gap-2", className), children: [_jsx("span", { className: "h-1.5 w-20 overflow-hidden rounded-full bg-secondary", "aria-hidden": true, children: _jsx("span", { className: "block h-full rounded-full bg-primary", style: { width: `${total === 0 ? 0 : Math.round((done / total) * 100)}%` } }) }), _jsxs("span", { className: "text-xs text-muted-foreground tabular-nums", children: [done, " of ", total, " done"] })] }));
66
+ }
67
+ /** `**label**` in bold and `_example_` in italics — the only markup the instructions use. */
68
+ export function HowText({ text }) {
69
+ const parts = text.split(/(\*\*[^*]+\*\*|_[^_]+_)/g).filter(Boolean);
70
+ return (_jsx(_Fragment, { children: parts.map((part, index) => part.startsWith("**") && part.endsWith("**") && part.length > 4 ? (_jsx("strong", { className: "font-semibold", children: part.slice(2, -2) }, index)) : part.startsWith("_") && part.endsWith("_") && part.length > 2 ? (_jsx("em", { children: part.slice(1, -1) }, index)) : (_jsx("span", { children: part }, index))) }));
71
+ }
72
+ /**
73
+ * The ⓘ: how to do this step, beside the step itself.
74
+ *
75
+ * Built here rather than on a popover library, because this package cannot pick
76
+ * one for six apps. It is a disclosure, not a dialog: Escape closes it, a click
77
+ * outside closes it, and focus is left where the person put it — nothing here
78
+ * is worth trapping focus for.
79
+ */
80
+ export function HowToButton({ how, guide, footer, className, classNames, }) {
81
+ const c = useClassNames(classNames);
82
+ const { Link } = useGuideChrome();
83
+ const [open, setOpen] = React.useState(false);
84
+ const wrapper = React.useRef(null);
85
+ const panelId = React.useId();
86
+ React.useEffect(() => {
87
+ if (!open)
88
+ return;
89
+ const onKeyDown = (event) => {
90
+ if (event.key === "Escape")
91
+ setOpen(false);
92
+ };
93
+ const onPointerDown = (event) => {
94
+ const node = event.target;
95
+ if (node instanceof Node && wrapper.current?.contains(node))
96
+ return;
97
+ setOpen(false);
98
+ };
99
+ document.addEventListener("keydown", onKeyDown);
100
+ document.addEventListener("mousedown", onPointerDown);
101
+ return () => {
102
+ document.removeEventListener("keydown", onKeyDown);
103
+ document.removeEventListener("mousedown", onPointerDown);
104
+ };
105
+ }, [open]);
106
+ return (_jsxs("span", { ref: wrapper, className: "relative inline-flex", children: [_jsx("button", { type: "button", onClick: () => setOpen((current) => !current), "aria-expanded": open, "aria-controls": open ? panelId : undefined, "aria-label": how.title, title: how.title, className: cx(c.iconButton, className), children: _jsx(Info, { className: "size-4", "aria-hidden": true }) }), open ? (_jsxs("span", { id: panelId, role: "group", "aria-label": how.title, className: c.popover, children: [_jsx("span", { className: cx(c.popoverTitle, "block"), children: how.title }), _jsx("ol", { className: "mt-3 flex flex-col gap-2", children: how.steps.map((step, index) => (_jsxs("li", { className: c.popoverStep, children: [_jsx("span", { "aria-hidden": true, className: c.popoverNumber, children: index + 1 }), _jsx("span", { children: _jsx(HowText, { text: step }) })] }, index))) }), footer, guide ? (_jsxs("span", { className: cx(c.popoverFooter, "flex"), children: [_jsxs(Link, { href: guide.href, target: "_blank", rel: "noopener", className: "inline-flex items-center gap-1.5 text-[13px] font-medium text-primary underline-offset-4 hover:underline", children: [_jsx(BookOpen, { className: "size-3.5", "aria-hidden": true }), guide.title, " guide"] }), _jsx("span", { className: "text-xs text-muted-foreground", children: "Opens in a new tab" })] })) : null] })) : null] }));
107
+ }
108
+ /** The book: the guide for the feature, in a new tab. */
109
+ export function GuideIconLink({ guide, className, classNames, }) {
110
+ const c = useClassNames(classNames);
111
+ const { Link } = useGuideChrome();
112
+ if (!guide)
113
+ return null;
114
+ const label = `Guide: ${guide.title}`;
115
+ return (_jsx(Link, { href: guide.href, target: "_blank", rel: "noopener", "aria-label": label, title: label, className: cx(c.iconButton, className), children: _jsx(BookOpen, { className: "size-4", "aria-hidden": true }) }));
116
+ }
117
+ /**
118
+ * Both icons for one step — or, for an item with several, its steps listed in
119
+ * the ⓘ and the first step's guide on the book.
120
+ */
121
+ export function ItemHelp({ item, className, classNames, }) {
122
+ const { words } = useGuideChrome();
123
+ const [first] = item.steps;
124
+ if (!first)
125
+ return null;
126
+ if (item.steps.length === 1) {
127
+ return (_jsxs("span", { className: cx("inline-flex items-center", className), children: [first.how ? _jsx(HowToButton, { how: first.how, guide: first.guide, classNames: classNames }) : null, _jsx(GuideIconLink, { guide: first.guide, classNames: classNames })] }));
128
+ }
129
+ return (_jsxs("span", { className: cx("inline-flex items-center", className), children: [_jsx(HowToButton, { classNames: classNames, how: {
130
+ title: `What “${item.title}” asks for`,
131
+ steps: item.steps.map((step) => `${step.done ? "Done — " : ""}**${step.title}**${step.reward > 0 ? ` · ${step.reward} ${rewardWord(step.reward, words)}` : ""}`),
132
+ }, guide: first.guide, footer: _jsxs("p", { className: "mt-3 text-xs text-muted-foreground", children: ["Each step, with how to do it, is on the screen it's done on.", item.paysOnComplete ? " Its rewards land once the whole thing is built." : ""] }) }), _jsx(GuideIconLink, { guide: first.guide, classNames: classNames })] }));
133
+ }
134
+ //# sourceMappingURL=bits.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bits.js","sourceRoot":"","sources":["../../src/setup-guide/bits.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,33 @@
1
+ import * as React from "react";
2
+ import { type ClassNameOverrides } from "./bits.js";
3
+ /** Replaces an item's — or one step's — default action, by its key. */
4
+ export type SetupActions = Partial<Record<string, React.ReactNode>>;
5
+ export interface SetupCardProps {
6
+ /**
7
+ * `full` leads a page that has little else on it: the band with what has been
8
+ * earned, and every item as a row. `compact` is one band above the day's work
9
+ * once the account has some, showing progress and what is next.
10
+ */
11
+ variant: "full" | "compact";
12
+ title: string;
13
+ lead: string;
14
+ /** Extra words under the progress in the band. */
15
+ aside?: React.ReactNode;
16
+ actions?: SetupActions;
17
+ /** An item whose steps are done on this very page, listed inside its row. */
18
+ inlineItemKey?: string;
19
+ /** What to show when there is no guide to draw — hidden, or an account without one. */
20
+ fallback?: React.ReactNode;
21
+ /** Shown when every step is done and paid, e.g. a link to what was earned. */
22
+ completeAction?: React.ReactNode;
23
+ /** How long the whole thing takes, if the app wants to promise. */
24
+ timeNote?: string;
25
+ /** A line about what the reward is for, under the ring. */
26
+ rewardNote?: string;
27
+ /** Replaces the band's background outright. `{}` falls back to the card's own. */
28
+ bandStyle?: React.CSSProperties;
29
+ className?: string;
30
+ classNames?: ClassNameOverrides;
31
+ }
32
+ export declare function SetupCard({ variant, title, lead, aside, actions, inlineItemKey, fallback, completeAction, timeNote, rewardNote, bandStyle, className, classNames, }: SetupCardProps): React.JSX.Element;
33
+ //# sourceMappingURL=card.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"card.d.ts","sourceRoot":"","sources":["../../src/setup-guide/card.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAG/B,OAAO,EAUL,KAAK,kBAAkB,EACxB,MAAM,WAAW,CAAC;AAKnB,uEAAuE;AACvE,MAAM,MAAM,YAAY,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC;AAEpE,MAAM,WAAW,cAAc;IAC7B;;;;OAIG;IACH,OAAO,EAAE,MAAM,GAAG,SAAS,CAAC;IAC5B,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,kDAAkD;IAClD,KAAK,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IACxB,OAAO,CAAC,EAAE,YAAY,CAAC;IACvB,6EAA6E;IAC7E,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,uFAAuF;IACvF,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC3B,8EAA8E;IAC9E,cAAc,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IACjC,mEAAmE;IACnE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,2DAA2D;IAC3D,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,kFAAkF;IAClF,SAAS,CAAC,EAAE,KAAK,CAAC,aAAa,CAAC;IAChC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,kBAAkB,CAAC;CACjC;AAED,wBAAgB,SAAS,CAAC,EACxB,OAAO,EACP,KAAK,EACL,IAAI,EACJ,KAAK,EACL,OAAY,EACZ,aAAa,EACb,QAAe,EACf,cAAc,EACd,QAAQ,EACR,UAAU,EACV,SAA4B,EAC5B,SAAS,EACT,UAAU,GACX,EAAE,cAAc,qBAiJhB"}