@danxbot/ui 2.2.2 → 2.3.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.
@@ -0,0 +1,37 @@
1
+ import { type ReactNode } from "react";
2
+ import type { StatusOption } from "../StatusSelect";
3
+ import type { ChecklistItem, ChecklistState, ChecklistTally } from "./types";
4
+ export type { ChecklistItem, ChecklistState, ChecklistTally };
5
+ export interface StatusChecklistProps<Key extends string = string> {
6
+ /**
7
+ * What this list IS — "Evidence for the March filing".
8
+ *
9
+ * Names the group, so somebody landing on it by keyboard learns what the
10
+ * counts are counting. Required for the same reason `KanbanBoard.label` is.
11
+ */
12
+ label: string;
13
+ /** The consumer's vocabulary, in the order the summary and groups read. */
14
+ states: readonly StatusOption<Key>[];
15
+ items: readonly ChecklistItem<Key>[];
16
+ /**
17
+ * `state` groups the rows under their state's heading; `given` renders them
18
+ * in the order supplied.
19
+ *
20
+ * GROUPED IS THE DEFAULT, and that is the opposite of what a checklist
21
+ * usually wants. A checklist you work down top to bottom should stay in its
22
+ * given order — but this control's stated job is to make the shape of the gap
23
+ * legible, and an interleaved list makes the reader do the sorting in their
24
+ * head while the summary above already claims to have done it for them.
25
+ * `given` is there for the case where the order is itself meaningful — a
26
+ * sequence, a priority, a filing order — because then regrouping destroys
27
+ * information the consumer put in deliberately.
28
+ */
29
+ order?: "state" | "given";
30
+ /** Shown when `items` is empty. `EmptyState` supplies the shape. */
31
+ emptyTitle?: ReactNode;
32
+ emptyDescription?: ReactNode;
33
+ emptyAction?: ReactNode;
34
+ emptyIcon?: ReactNode;
35
+ className?: string;
36
+ }
37
+ export declare function StatusChecklist<Key extends string = string>({ label, states, items, order, emptyTitle, emptyDescription, emptyAction, emptyIcon, className, }: StatusChecklistProps<Key>): import("react").JSX.Element;
@@ -0,0 +1,3 @@
1
+ export type { ChecklistItem, ChecklistState, ChecklistTally } from "./types";
2
+ export { tallyChecklist } from "./tally";
3
+ export { StatusChecklist, type StatusChecklistProps } from "./StatusChecklist";
@@ -0,0 +1,19 @@
1
+ import type { StatusOption } from "../StatusSelect";
2
+ import type { ChecklistItem, ChecklistTally } from "./types";
3
+ /**
4
+ * Split `items` across the declared `states`, in the order the states were
5
+ * given.
6
+ *
7
+ * Every state appears in the result, INCLUDING EMPTY ONES. A zero is
8
+ * information — "nothing is blocking" is the single most useful thing this
9
+ * control ever says — and a chip that vanishes at zero is invisible at exactly
10
+ * the moment its emptiness is the news, while also making the summary row
11
+ * change width every time an item moves. The BODY is the other way round: see
12
+ * `StatusChecklist`.
13
+ *
14
+ * Throws rather than tolerating: an unknown state key, a duplicate state, a
15
+ * duplicate item id, or no states at all. Each of those renders as something
16
+ * plausible if allowed through — an item silently dropped from a list whose
17
+ * whole purpose is to be exhaustive is the worst failure this component has.
18
+ */
19
+ export declare function tallyChecklist<Key extends string>(states: readonly StatusOption<Key>[], items: readonly ChecklistItem<Key>[]): ChecklistTally<Key>[];
@@ -0,0 +1,28 @@
1
+ import type { ReactNode } from "react";
2
+ import type { StatusOption } from "../StatusSelect";
3
+ export type ChecklistState<Key extends string = string> = StatusOption<Key>;
4
+ export interface ChecklistItem<Key extends string = string> {
5
+ id: string;
6
+ /** What the item IS. The row's own text — never replaced by an aria-label. */
7
+ label: ReactNode;
8
+ /** Must be the `value` of one of the declared states. An unknown key throws. */
9
+ state: Key;
10
+ /** A second line: who it is with, when it was asked for, why it is missing. */
11
+ detail?: ReactNode;
12
+ /**
13
+ * A control for this row — "Request it", "Open", "Mark received".
14
+ *
15
+ * A slot rather than a `{label, onClick}` prop, for the same reason
16
+ * `RecordCard.badges` is one: whether the action is a button, a link, a menu
17
+ * or a spinner belongs to the surface, and every shape this prop could grow
18
+ * to cover would be a library release for a decision the consumer had
19
+ * already made.
20
+ */
21
+ action?: ReactNode;
22
+ }
23
+ /** One state, its option, and everything in it — in the declared order. */
24
+ export interface ChecklistTally<Key extends string = string> {
25
+ option: StatusOption<Key>;
26
+ count: number;
27
+ items: ChecklistItem<Key>[];
28
+ }
@@ -0,0 +1,44 @@
1
+ import { type ReactNode } from "react";
2
+ import type { Step, StepStatus, StepperProgress } from "./types";
3
+ export type { Step, StepStatus, StepperProgress };
4
+ export interface StepperProps {
5
+ /**
6
+ * What this flow IS — "Case intake", "Deployment".
7
+ *
8
+ * It names the ordered list, which is how somebody arriving at the rail
9
+ * learns what the sequence is for. Required rather than optional for the same
10
+ * reason `KanbanBoard.label` is: an unnamed list announces as "list" and
11
+ * gives nobody a reason to be in it.
12
+ */
13
+ label: string;
14
+ steps: readonly Step[];
15
+ /** 0-based. Out of range throws — see `assertSteps`. */
16
+ current: number;
17
+ /**
18
+ * Move the cursor.
19
+ *
20
+ * ABSENT MAKES THE WHOLE RAIL A READ-OUT: no step is clickable and no
21
+ * forward/back control is drawn. The configuration is the switch, the same
22
+ * way `UseCaseCard` only becomes editable when handed `onStatusChange`. A
23
+ * consumer driving its own buttons omits this and keeps the rail.
24
+ */
25
+ onCurrentChange?: (index: number) => void;
26
+ /**
27
+ * What the last step's forward control does. Absent means no control is drawn
28
+ * on the last step — a wizard whose final action lives in its own form.
29
+ */
30
+ onFinish?: () => void;
31
+ backLabel?: string;
32
+ nextLabel?: string;
33
+ finishLabel?: string;
34
+ /**
35
+ * `vertical` stacks the rail beside long-form content. `horizontal` is the
36
+ * default because a sequence reads left to right, which is also the only
37
+ * layout in which the connector between two steps means anything.
38
+ */
39
+ orientation?: "horizontal" | "vertical";
40
+ /** The CURRENT step's content. */
41
+ children?: ReactNode;
42
+ className?: string;
43
+ }
44
+ export declare function Stepper({ label, steps, current, onCurrentChange, onFinish, backLabel, nextLabel, finishLabel, orientation, children, className, }: StepperProps): import("react").JSX.Element;
@@ -0,0 +1,48 @@
1
+ import type { Step, StepStatus, StepperProgress } from "./types";
2
+ /**
3
+ * Reject a flow that cannot be rendered honestly, loudly.
4
+ *
5
+ * Every one of these is unreachable through the component's own UI and silent
6
+ * if allowed through: an empty rail renders as a blank strip, an out-of-range
7
+ * cursor renders no current step at all, duplicate ids give React two elements
8
+ * with one key (which reconciles them into each other on the next change), and
9
+ * an empty `blocked` string disables the forward button while printing nothing
10
+ * beside it.
11
+ */
12
+ export declare function assertSteps(steps: readonly Step[], current: number): void;
13
+ /**
14
+ * What one step IS, given where the cursor is.
15
+ *
16
+ * PRECEDENCE, and the first rule is the interesting one. `blocked` outranks
17
+ * everything, including `complete`, wherever the step sits. Data saying both
18
+ * is contradictory, and when a record contradicts itself the exception is what
19
+ * the reader needs — the same call `ProvenanceBadge` makes when it stays quiet
20
+ * on the ordinary case and loud on every other one. An affirmative that hides
21
+ * an obstruction is the worse of the two possible mistakes.
22
+ *
23
+ * Note that a `blocked` reason on a step OTHER than the current one still
24
+ * renders as blocked but gates nothing: only the current step's reason can stop
25
+ * forward movement (see `advanceBlockReason`). That is deliberate — a flow can
26
+ * legitimately want to say "and this one ahead needs a signature" without
27
+ * claiming that is why you are stuck right now.
28
+ */
29
+ export declare function stepStatus(steps: readonly Step[], current: number, index: number): StepStatus;
30
+ /**
31
+ * Why forward movement is refused, or `null` when it is not.
32
+ *
33
+ * ONLY THE CURRENT STEP GATES. A reason on a step you are not standing on is
34
+ * information about that step, not a claim about this one.
35
+ *
36
+ * `null` is the only "not blocked" value — an empty string is rejected at
37
+ * `assertSteps` rather than treated as absent, so a falsy check here cannot
38
+ * turn a reason that failed to load into permission to proceed.
39
+ */
40
+ export declare function advanceBlockReason(steps: readonly Step[], current: number): string | null;
41
+ /**
42
+ * Position and completion, as two numbers that are allowed to disagree.
43
+ *
44
+ * This exists so a consumer that genuinely wants a bar builds it from a named
45
+ * number instead of guessing which one a bar meant. The component itself draws
46
+ * no percentage — see the note on `Stepper`.
47
+ */
48
+ export declare function stepperProgress(steps: readonly Step[], current: number): StepperProgress;
@@ -0,0 +1,3 @@
1
+ export * from "./types";
2
+ export * from "./derive";
3
+ export { Stepper, type StepperProps } from "./Stepper";
@@ -0,0 +1,70 @@
1
+ import type { ReactNode } from "react";
2
+ export interface Step {
3
+ id: string;
4
+ /** Shown on the rail and spoken as the step's name. */
5
+ label: string;
6
+ /** What this step is for. One line, under the label. */
7
+ hint?: ReactNode;
8
+ /**
9
+ * Finished.
10
+ *
11
+ * ABSENT MEANS NOT COMPLETE. There is no third state, and none is invented:
12
+ * nobody has ever needed to record that they do not know whether a step was
13
+ * finished. This is the same call `RoadmapItem.built` makes about an absent
14
+ * track key, and it is what keeps `!step.complete` from having two readings.
15
+ */
16
+ complete?: boolean;
17
+ /**
18
+ * Why the flow cannot move on from here. Absent means it can.
19
+ *
20
+ * Written for the person who would have pressed the button — "two documents
21
+ * are still missing", not "invalid". It is rendered beside the forward
22
+ * control rather than only disabling it, because a dead button with no
23
+ * sentence next to it is indistinguishable from a broken one.
24
+ *
25
+ * AN EMPTY STRING THROWS, and that is a deliberate divergence from
26
+ * `KanbanLane.refusal`, which accepts one. There the reason is supporting
27
+ * detail on a lane that is already visibly refusing; here the sentence IS the
28
+ * mechanism — an empty one produces exactly the mute disabled button this
29
+ * prop exists to prevent, so it is rejected loudly instead of rendered as
30
+ * nothing.
31
+ */
32
+ blocked?: string;
33
+ }
34
+ /**
35
+ * What a step IS right now — derived from the steps and the cursor, never
36
+ * stored.
37
+ *
38
+ * Five, not two, because "finished", "you are here", "you skipped it", "not
39
+ * reached yet" and "this is what is stopping you" call for four different
40
+ * next actions and one absence of action. Collapsing them to a boolean is what
41
+ * produces a rail where a skipped step and an untouched one look identical.
42
+ */
43
+ export type StepStatus =
44
+ /** `complete: true`. */
45
+ "complete"
46
+ /** Carries a `blocked` reason. Beats every other reading — see `stepStatus`. */
47
+ | "blocked"
48
+ /** The cursor is on it. */
49
+ | "current"
50
+ /** Behind the cursor and not complete: passed over. */
51
+ | "incomplete"
52
+ /** Ahead of the cursor. Not a fault — simply not reached. */
53
+ | "upcoming";
54
+ /**
55
+ * The two numbers a stepper header is tempted to average into one bar.
56
+ *
57
+ * `position` is where the cursor is; `complete` is how many are finished. They
58
+ * disagree the moment anybody moves past a step without finishing it, and
59
+ * `skipped` is that disagreement stated as a number rather than hidden inside
60
+ * a percentage.
61
+ */
62
+ export interface StepperProgress {
63
+ /** 1-based, for reading: "Step 3 of 5". */
64
+ position: number;
65
+ total: number;
66
+ /** How many steps are `complete`, anywhere in the flow. */
67
+ complete: number;
68
+ /** Behind the cursor and not complete. */
69
+ skipped: number;
70
+ }
@@ -41,6 +41,8 @@ export { RecordCard, type RecordCardProps, type RecordCardMetric, } from "./comp
41
41
  export { Tabs, TabList, Tab, TabPanel, type TabsProps, type TabListProps, type TabProps, type TabPanelProps, } from "./components/Tabs";
42
42
  export { Accordion, AccordionItem, type AccordionProps, type AccordionItemProps, } from "./components/Accordion";
43
43
  export { Breadcrumb, Pagination, type BreadcrumbProps, type PaginationProps, type Crumb, } from "./components/Navigation";
44
+ export { Stepper, stepStatus, stepperProgress, advanceBlockReason, assertSteps, type StepperProps, type Step, type StepStatus, type StepperProgress, } from "./components/stepper";
45
+ export { StatusChecklist, tallyChecklist, type StatusChecklistProps, type ChecklistItem, type ChecklistState, type ChecklistTally, } from "./components/checklist";
44
46
  export { Toggle, ToggleGroup, Toolbar, ToolbarSeparator, type ToggleProps, type ToggleGroupProps, type ToolbarProps, } from "./components/Toggle";
45
47
  export { Separator, ScrollArea, Collapsible, EmptyState, Timeline, type SeparatorProps, type ScrollAreaProps, type CollapsibleProps, type EmptyStateProps, type TimelineProps, type TimelineItem, } from "./components/Surface";
46
48
  export { PanelGroup, Panel, PanelResizer, type PanelGroupProps, type PanelProps, type PanelResizerProps, type PanelDirection, type PanelNarrowMode, } from "./components/Panel";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@danxbot/ui",
3
- "version": "2.2.2",
3
+ "version": "2.3.0",
4
4
  "type": "module",
5
5
  "description": "Danxbot — a domain-agnostic React design system with motion as a first-class primitive.",
6
6
  "license": "MIT",