@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
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** —
|
|
339
|
-
|
|
340
|
-
|
|
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/)(?!`.
|
|
414
|
-
|
|
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.
|
|
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"}
|