@charcuterie/ui 3.20.0 → 3.22.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 (43) hide show
  1. package/README.md +11 -0
  2. package/dist/Board/BoardCard.d.ts +42 -18
  3. package/dist/Board/BoardCard.d.ts.map +1 -1
  4. package/dist/Board/BoardCard.js +17 -1
  5. package/dist/Board/BoardCard.js.map +1 -1
  6. package/dist/DropRail/DropRail.d.ts +109 -0
  7. package/dist/DropRail/DropRail.d.ts.map +1 -0
  8. package/dist/DropRail/DropRail.js +277 -0
  9. package/dist/DropRail/DropRail.js.map +1 -0
  10. package/dist/NavBar/NavBar.d.ts +130 -0
  11. package/dist/NavBar/NavBar.d.ts.map +1 -0
  12. package/dist/NavBar/NavBar.js +114 -0
  13. package/dist/NavBar/NavBar.js.map +1 -0
  14. package/dist/NavBar/NavBarLink.d.ts +43 -0
  15. package/dist/NavBar/NavBarLink.d.ts.map +1 -0
  16. package/dist/NavBar/NavBarLink.js +67 -0
  17. package/dist/NavBar/NavBarLink.js.map +1 -0
  18. package/dist/NavBar/navBarCurrent.d.ts +37 -0
  19. package/dist/NavBar/navBarCurrent.d.ts.map +1 -0
  20. package/dist/NavBar/navBarCurrent.js +48 -0
  21. package/dist/NavBar/navBarCurrent.js.map +1 -0
  22. package/dist/index.d.ts +5 -0
  23. package/dist/index.d.ts.map +1 -1
  24. package/dist/index.js +3 -0
  25. package/dist/index.js.map +1 -1
  26. package/package.json +1 -1
  27. package/src/Board/Board.stories.tsx +100 -0
  28. package/src/Board/Board.test.tsx +62 -0
  29. package/src/Board/BoardCard.tsx +71 -18
  30. package/src/DropRail/DropRail.mdx +90 -0
  31. package/src/DropRail/DropRail.stories.tsx +328 -0
  32. package/src/DropRail/DropRail.test.tsx +154 -0
  33. package/src/DropRail/DropRail.tsx +496 -0
  34. package/src/NavBar/NavBar.mdx +157 -0
  35. package/src/NavBar/NavBar.stories.tsx +350 -0
  36. package/src/NavBar/NavBar.test.tsx +344 -0
  37. package/src/NavBar/NavBar.tsx +271 -0
  38. package/src/NavBar/NavBarLink.tsx +139 -0
  39. package/src/NavBar/navBarCurrent.test.ts +81 -0
  40. package/src/NavBar/navBarCurrent.ts +64 -0
  41. package/src/icons.storyHelpers.tsx +12 -0
  42. package/src/index.ts +11 -0
  43. package/src/sourceRules.test.ts +24 -1
package/README.md CHANGED
@@ -48,6 +48,17 @@ Moving a card takes **no drag-and-drop dependency**: one handle per card, a `Men
48
48
  other lanes as the primary path, Pointer-Event dragging at 1.4 KB gzip beside it
49
49
  ([decision](../../docs/decisions/2026-08-19-the-board-owns-the-move-and-takes-no-drag-and-drop-dependency.md)).
50
50
 
51
+ **`DropRail` is the move control for a page `Board`'s `Menu` cannot serve.** A menu of the
52
+ other lanes is right at three lanes; it is wrong at thirty-four groups, where it becomes a
53
+ scrolling list inside a popup and a drag becomes a scroll with the button held down. The
54
+ rail pins every destination to the top for the length of a move — no travel, nothing off
55
+ screen — as a `listbox` driven by pointer, tap **or** the arrow keys, all committing through
56
+ one `onPick`. It reads `event.buttons` to tell a drag from a tap, so a host says only
57
+ whether a move is in flight. It holds the same line on dependencies: `elementFromPoint` over
58
+ its own chips is the only geometry a rail needs. Not to be confused with `Rail`, the app
59
+ shell's side landmark — `FileDropZone` is the precedent for the prefix carrying the
60
+ distinction.
61
+
51
62
  **`BadgeButton` is a `Badge` you can press**, and it came from a consumer rather than from a
52
63
  plan: QueuePilot has six pill-shaped controls — per-entry setting tags, an Edit chip, two
53
64
  start-point chips, a group chip, a pool's Exclude chip — that were hand-rolled
@@ -1,23 +1,8 @@
1
1
  import type { IntentName } from "@charcuterie/tokens";
2
2
  import type { ReactNode } from "react";
3
+ import type { CardAccentEdge } from "../Card/cardAccentEdge.ts";
3
4
  type BoardItemFields = {
4
- /**
5
- * The leading colour bar. Priority, in the consumer this was built
6
- * for — *"Priority is styled as a leading colour bar, and **never**
7
- * a coloured word before the title."*
8
- *
9
- * Pair it with `accentLabel`. A bar is colour and nothing else, so
10
- * on its own it fails WCAG 1.4.1 outright and is invisible to
11
- * every screen reader; the label rides along in a
12
- * `VisuallyHidden` so the same fact reaches both channels. Docket's
13
- * first board painted these `--color-danger-9` — a Radix-style
14
- * scale this token set has never had — so every bar was
15
- * transparent while every "is it rendered" assertion passed. The
16
- * families here are `--color-intent-<intent>-<role>`; there is no
17
- * numeric scale to reach for.
18
- */
19
- accentIntent?: IntentName;
20
- /** What the bar means, in words. "Priority 0". */
5
+ /** What the bar means, in words. "Priority 0", "Movies and Shows". */
21
6
  accentLabel?: string;
22
7
  /**
23
8
  * Under the title and metadata, spanning the card: a live run
@@ -55,6 +40,45 @@ type BoardItemFields = {
55
40
  /** The trailing cluster: an assignee avatar, an elapsed-time chip. */
56
41
  trailing?: ReactNode;
57
42
  };
43
+ /**
44
+ * The leading colour bar, in one of two shapes — and the second one
45
+ * exists because the first cannot follow a corner.
46
+ *
47
+ * `accentIntent` is an inset pill: a `w-1` span inside the card,
48
+ * beside the content. It was built for priority — *"Priority is
49
+ * styled as a leading colour bar, and **never** a coloured word
50
+ * before the title."* An intent is a claim the design system makes
51
+ * (`danger` says what happens if you press the thing), so this arm
52
+ * is for a bar that means a STATE.
53
+ *
54
+ * `accentEdge` is the card's own edge — `Card`'s treatment, the same
55
+ * pseudo-element, so it takes `border-radius: inherit` and wraps the
56
+ * corner at `cq-lg` where this card grows one. It carries a
57
+ * CATEGORICAL colour or a computed one, which is what an identity
58
+ * needs: a project, a repo, a source. The owner asked for exactly
59
+ * this on Docket's board — *"The Board has no colors. That could
60
+ * use project colors on the sides at least."* — after reporting the
61
+ * same treatment missing from the task rows.
62
+ *
63
+ * The two are mutually exclusive by type, not by convention. Both
64
+ * paint a leading bar in the same place, and a card wearing two of
65
+ * them is a card whose leading edge means two things.
66
+ *
67
+ * ⚠️ Either arm pairs with `accentLabel`. A bar is colour and
68
+ * nothing else, so on its own it fails WCAG 1.4.1 outright and is
69
+ * invisible to every screen reader; the label rides along in a
70
+ * `VisuallyHidden` so the same fact reaches both channels. Docket's
71
+ * first board painted its bars `--color-danger-9` — a Radix-style
72
+ * scale this token set has never had — so every bar was transparent
73
+ * while every "is it rendered" assertion passed.
74
+ */
75
+ type BoardItemAccent = {
76
+ accentEdge?: never;
77
+ accentIntent?: IntentName;
78
+ } | {
79
+ accentEdge: CardAccentEdge;
80
+ accentIntent?: never;
81
+ };
58
82
  /**
59
83
  * Who owns the card title's link — the board, or the caller.
60
84
  *
@@ -98,7 +122,7 @@ type BoardItemTitleLink = {
98
122
  */
99
123
  titleContent: ReactNode;
100
124
  };
101
- export type BoardItem = BoardItemFields & BoardItemTitleLink;
125
+ export type BoardItem = BoardItemAccent & BoardItemFields & BoardItemTitleLink;
102
126
  export type BoardCardProps = {
103
127
  item: BoardItem;
104
128
  laneLabel: string;
@@ -1 +1 @@
1
- {"version":3,"file":"BoardCard.d.ts","sourceRoot":"","sources":["../../src/Board/BoardCard.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAA;AACrD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AAatC,KAAK,eAAe,GAAG;IACrB;;;;;;;;;;;;;;OAcG;IACH,YAAY,CAAC,EAAE,UAAU,CAAA;IACzB,kDAAkD;IAClD,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB;;;OAGG;IACH,MAAM,CAAC,EAAE,SAAS,CAAA;IAClB;;;;;OAKG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,GAAG,EAAE,MAAM,CAAA;IACX;;;;OAIG;IACH,KAAK,CAAC,EAAE,SAAS,CAAA;IACjB;;;OAGG;IACH,IAAI,CAAC,EAAE,SAAS,CAAA;IAChB;;;;;;;OAOG;IACH,KAAK,EAAE,MAAM,CAAA;IACb,sEAAsE;IACtE,QAAQ,CAAC,EAAE,SAAS,CAAA;CACrB,CAAA;AAED;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,KAAK,kBAAkB,GACnB;IACE,sEAAsE;IACtE,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,sEAAsE;IACtE,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAA;IACrB,YAAY,CAAC,EAAE,KAAK,CAAA;CACrB,GACD;IACE,IAAI,CAAC,EAAE,KAAK,CAAA;IACZ,QAAQ,CAAC,EAAE,KAAK,CAAA;IAChB;;;;;;;;OAQG;IACH,YAAY,EAAE,SAAS,CAAA;CACxB,CAAA;AAEL,MAAM,MAAM,SAAS,GAAG,eAAe,GAAG,kBAAkB,CAAA;AAE5D,MAAM,MAAM,cAAc,GAAG;IAC3B,IAAI,EAAE,SAAS,CAAA;IACf,SAAS,EAAE,MAAM,CAAA;IACjB;;;;;;;;OAQG;IACH,QAAQ,CAAC,EAAE,SAAS,CAAA;IACpB,0DAA0D;IAC1D,WAAW,EAAE,SAAS;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,EAAE,CAAA;IACtD,8DAA8D;IAC9D,YAAY,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,CAAA;IAC1C,WAAW,CAAC,EAAE,CAAC,YAAY,EAAE;QAC3B,OAAO,EAAE,MAAM,CAAA;QACf,OAAO,EAAE,MAAM,CAAA;QACf,aAAa,EAAE,WAAW,GAAG,OAAO,CAAA;QACpC,SAAS,EAAE,MAAM,CAAA;KAClB,KAAK,IAAI,CAAA;CACX,CAAA;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCG;AACH,eAAO,MAAM,SAAS,GAAI,wEAOvB,cAAc,KAAG,SA4JnB,CAAA"}
1
+ {"version":3,"file":"BoardCard.d.ts","sourceRoot":"","sources":["../../src/Board/BoardCard.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAA;AACrD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AAItC,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAA;AAc/D,KAAK,eAAe,GAAG;IACrB,sEAAsE;IACtE,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB;;;OAGG;IACH,MAAM,CAAC,EAAE,SAAS,CAAA;IAClB;;;;;OAKG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,GAAG,EAAE,MAAM,CAAA;IACX;;;;OAIG;IACH,KAAK,CAAC,EAAE,SAAS,CAAA;IACjB;;;OAGG;IACH,IAAI,CAAC,EAAE,SAAS,CAAA;IAChB;;;;;;;OAOG;IACH,KAAK,EAAE,MAAM,CAAA;IACb,sEAAsE;IACtE,QAAQ,CAAC,EAAE,SAAS,CAAA;CACrB,CAAA;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,KAAK,eAAe,GAChB;IAAE,UAAU,CAAC,EAAE,KAAK,CAAC;IAAC,YAAY,CAAC,EAAE,UAAU,CAAA;CAAE,GACjD;IAAE,UAAU,EAAE,cAAc,CAAC;IAAC,YAAY,CAAC,EAAE,KAAK,CAAA;CAAE,CAAA;AAExD;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,KAAK,kBAAkB,GACnB;IACE,sEAAsE;IACtE,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,sEAAsE;IACtE,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAA;IACrB,YAAY,CAAC,EAAE,KAAK,CAAA;CACrB,GACD;IACE,IAAI,CAAC,EAAE,KAAK,CAAA;IACZ,QAAQ,CAAC,EAAE,KAAK,CAAA;IAChB;;;;;;;;OAQG;IACH,YAAY,EAAE,SAAS,CAAA;CACxB,CAAA;AAEL,MAAM,MAAM,SAAS,GAAG,eAAe,GACrC,eAAe,GACf,kBAAkB,CAAA;AAEpB,MAAM,MAAM,cAAc,GAAG;IAC3B,IAAI,EAAE,SAAS,CAAA;IACf,SAAS,EAAE,MAAM,CAAA;IACjB;;;;;;;;OAQG;IACH,QAAQ,CAAC,EAAE,SAAS,CAAA;IACpB,0DAA0D;IAC1D,WAAW,EAAE,SAAS;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,EAAE,CAAA;IACtD,8DAA8D;IAC9D,YAAY,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,CAAA;IAC1C,WAAW,CAAC,EAAE,CAAC,YAAY,EAAE;QAC3B,OAAO,EAAE,MAAM,CAAA;QACf,OAAO,EAAE,MAAM,CAAA;QACf,aAAa,EAAE,WAAW,GAAG,OAAO,CAAA;QACpC,SAAS,EAAE,MAAM,CAAA;KAClB,KAAK,IAAI,CAAA;CACX,CAAA;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCG;AACH,eAAO,MAAM,SAAS,GAAI,wEAOvB,cAAc,KAAG,SAsLnB,CAAA"}
@@ -1,6 +1,7 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { useState } from "react";
3
3
  import { Button } from "../Button/Button.js";
4
+ import { getAccentEdgeClassName, getAccentEdgeStyle, } from "../Card/cardAccentEdge.js";
4
5
  import { FOCUS_RING_CLASS, INTENT_SOLID_FILL_CLASS, } from "../intentStyles.js";
5
6
  import { Menu } from "../Menu/Menu.js";
6
7
  import { TextLink } from "../TextLink/TextLink.js";
@@ -52,7 +53,22 @@ export const BoardCard = ({ item, laneLabel, moveIcon, moveTargets, onMoveToLane
52
53
  // divider goes and each card grows its own box. The *list*
53
54
  // cannot do this to itself — it is the container, and a
54
55
  // container query never matches its own container.
55
- "cq-lg:mb-2 cq-lg:rounded-lg cq-lg:border cq-lg:bg-surface-raised cq-lg:p-3 cq-lg:shadow-low", item.isDimmed && "opacity-60"), "data-board-card": item.key, children: [item.accentIntent ? (_jsx("span", { className: toClassName("w-1 shrink-0 self-stretch rounded-full", INTENT_SOLID_FILL_CLASS[item.accentIntent]), children: item.accentLabel ? (_jsx(VisuallyHidden, { children: item.accentLabel })) : null })) : null, _jsxs("div", { className: "flex min-w-0 flex-1 flex-col gap-1", children: [_jsxs("div", { className: "flex min-w-0 items-start gap-2", children: [_jsxs("div", { className: "flex min-w-0 flex-1 flex-col gap-1 cq-sm:flex-row cq-sm:items-center cq-sm:gap-2", children: [_jsxs("div", { className: "flex min-w-0 flex-1 items-start gap-1.5 text-sm leading-snug", children: [item.marks ? (_jsx("span", { className: "flex shrink-0 items-center gap-1 pt-1", children: item.marks })) : null, item.href ? (_jsx(TextLink, { appearance: "standalone", className: "min-w-0 max-w-full font-normal", href: item.href, intent: "neutral", children: titleBlock })) : item.onSelect ? (_jsx("button", { className: toClassName("min-w-0 max-w-full cursor-pointer rounded-xs text-start text-intent-neutral-content hover:underline", FOCUS_RING_CLASS), onClick: item.onSelect, type: "button", children: titleBlock })) : (titleBlock)] }), item.meta ? (_jsx("div", { className: "flex min-w-0 flex-wrap items-center gap-1 cq-sm:shrink-0 cq-sm:flex-nowrap cq-sm:justify-end", children: item.meta })) : null] }), item.trailing || onMoveToLane ? (_jsxs("div", { className: "flex shrink-0 items-center gap-1", children: [item.trailing, onMoveToLane && moveTargets.length > 0 ? (_jsx(Menu, { isVisible: isMenuVisible, items: moveTargets.map((target) => ({
56
+ "cq-lg:mb-2 cq-lg:rounded-lg cq-lg:border cq-lg:bg-surface-raised cq-lg:p-3 cq-lg:shadow-low",
57
+ // The edge is drawn on THIS box, which is why it needs no
58
+ // shape of its own: the box is a row below `cq-lg` and a
59
+ // rounded card above it, and `border-radius: inherit` gives
60
+ // the right answer at both — a straight stripe down a row
61
+ // that has no corner, and a bar that wraps the card's
62
+ // corner once it grows one.
63
+ //
64
+ // `ps-2` is what keeps the content off it. The bar is 3px
65
+ // and overlays rather than displaces, so without this the
66
+ // first mark sits on top of it; at `cq-lg` the card's own
67
+ // `p-3` is already wider than the bar.
68
+ item.accentEdge != null &&
69
+ toClassName("ps-2", getAccentEdgeClassName(item.accentEdge)), item.isDimmed && "opacity-60"), "data-board-card": item.key, ...(item.accentEdge == null
70
+ ? {}
71
+ : { style: getAccentEdgeStyle(item.accentEdge) }), children: [item.accentEdge != null && item.accentLabel ? (_jsx(VisuallyHidden, { children: item.accentLabel })) : null, item.accentIntent ? (_jsx("span", { className: toClassName("w-1 shrink-0 self-stretch rounded-full", INTENT_SOLID_FILL_CLASS[item.accentIntent]), children: item.accentLabel ? (_jsx(VisuallyHidden, { children: item.accentLabel })) : null })) : null, _jsxs("div", { className: "flex min-w-0 flex-1 flex-col gap-1", children: [_jsxs("div", { className: "flex min-w-0 items-start gap-2", children: [_jsxs("div", { className: "flex min-w-0 flex-1 flex-col gap-1 cq-sm:flex-row cq-sm:items-center cq-sm:gap-2", children: [_jsxs("div", { className: "flex min-w-0 flex-1 items-start gap-1.5 text-sm leading-snug", children: [item.marks ? (_jsx("span", { className: "flex shrink-0 items-center gap-1 pt-1", children: item.marks })) : null, item.href ? (_jsx(TextLink, { appearance: "standalone", className: "min-w-0 max-w-full font-normal", href: item.href, intent: "neutral", children: titleBlock })) : item.onSelect ? (_jsx("button", { className: toClassName("min-w-0 max-w-full cursor-pointer rounded-xs text-start text-intent-neutral-content hover:underline", FOCUS_RING_CLASS), onClick: item.onSelect, type: "button", children: titleBlock })) : (titleBlock)] }), item.meta ? (_jsx("div", { className: "flex min-w-0 flex-wrap items-center gap-1 cq-sm:shrink-0 cq-sm:flex-nowrap cq-sm:justify-end", children: item.meta })) : null] }), item.trailing || onMoveToLane ? (_jsxs("div", { className: "flex shrink-0 items-center gap-1", children: [item.trailing, onMoveToLane && moveTargets.length > 0 ? (_jsx(Menu, { isVisible: isMenuVisible, items: moveTargets.map((target) => ({
56
72
  key: target.key,
57
73
  label: target.label,
58
74
  onSelect: () => {
@@ -1 +1 @@
1
- {"version":3,"file":"BoardCard.js","sourceRoot":"","sources":["../../src/Board/BoardCard.tsx"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"BoardCard.js","sourceRoot":"","sources":["../../src/Board/BoardCard.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,109 @@
1
+ import type { ReactNode } from "react";
2
+ export type DropRailTarget = {
3
+ /**
4
+ * A number after the label — a lane's size, a project's open
5
+ * count. Optional, and absent is a real answer: a rail of
6
+ * destinations that have no size should not print a zero on every
7
+ * chip.
8
+ */
9
+ count?: number;
10
+ /**
11
+ * Where the thing being moved already is. It stays **visible and
12
+ * unofferable** rather than being filtered out, because a rail
13
+ * whose contents change depending on what you grabbed is a rail
14
+ * whose chips move under the pointer between one move and the
15
+ * next — and muscle memory is the only reason this control is
16
+ * faster than a menu.
17
+ */
18
+ isCurrent?: boolean;
19
+ isDisabled?: boolean;
20
+ key: string;
21
+ label: string;
22
+ /** A leading glyph the app owns — an emoji, a dot, an icon. */
23
+ mark?: ReactNode;
24
+ };
25
+ export type DropRailProps = {
26
+ className?: string;
27
+ /**
28
+ * Whether the rail is showing. The host owns this, because only
29
+ * the host knows a move started — and it is the same boolean
30
+ * whether the move began as a pointer drag or as a tap on a
31
+ * handle.
32
+ */
33
+ isOpen: boolean;
34
+ /** The rail's accessible name. Required — it is a listbox. */
35
+ label: string;
36
+ onDismiss?: () => void;
37
+ onPick: (targetKey: string) => void;
38
+ targets: readonly DropRailTarget[];
39
+ };
40
+ /**
41
+ * Every destination at once, brought to the pointer instead of the
42
+ * pointer being carried to it.
43
+ *
44
+ * ### The measurement this exists for
45
+ *
46
+ * `Board` moves a card with a `Menu` on the card, and that is the
47
+ * right control for a board: three or four lanes, all on screen, and
48
+ * the menu opens where your hand already is. It stops being the
49
+ * right control at the size the fleet's real lists reach. Docket's
50
+ * Backlog draws **34 project groups down one page**, of which about
51
+ * four fit on a display. Dragging a card from the first to the
52
+ * thirty-fourth means holding a pointer down while the page scrolls,
53
+ * and a menu of 34 items is a scrolling list inside a popup — the
54
+ * same problem in a smaller box.
55
+ *
56
+ * A rail answers both. It pins to the top for the length of the
57
+ * move, so nothing is ever off screen, and there is no travel: every
58
+ * destination is the same short distance away.
59
+ *
60
+ * ### One control, three drivers, and that is the whole design
61
+ *
62
+ * WCAG 2.5.7 requires a single-pointer alternative to any dragging
63
+ * movement, and `FileDropZone` next door says the same thing about
64
+ * its own gesture: build the accessible path first and let the drag
65
+ * be the enhancement. So the rail is **not** a drag affordance with
66
+ * a fallback bolted on. It is a listbox that three things drive:
67
+ *
68
+ * 1. **Pointer.** While a button is held, the chip under the
69
+ * pointer is active and releasing over it commits.
70
+ * 2. **Tap.** Opened with no button held, it is a list you touch.
71
+ * Identical markup, identical commit.
72
+ * 3. **Keyboard.** Arrow keys walk it, Enter commits, Escape
73
+ * abandons. `useRovingFocus` owns which chip is tabbable, so
74
+ * there is no second keyboard model in this file to drift out of
75
+ * step with `Listbox`.
76
+ *
77
+ * The rail does not know which of the three opened it, and the host
78
+ * does not have to say. It reads `event.buttons` on the first
79
+ * pointer event it sees: a button already down means a drag is in
80
+ * flight, and nothing else needs threading through.
81
+ *
82
+ * ### It takes no drag-and-drop dependency, and it hit-tests chips
83
+ *
84
+ * The board's decision priced four libraries and took none of them,
85
+ * because a published library's dependency list is inherited by
86
+ * every consumer — including one with 60 KB gzipped to spend in
87
+ * total. Nothing here changes that arithmetic, and this needs far
88
+ * less than `Board` does: a chip is a box under the pointer, so
89
+ * `elementFromPoint` answers the only geometric question there is.
90
+ * No snapshot, no collision strategy, no sortable transform.
91
+ *
92
+ * ### It is not `Rail`, and the two must not be confused
93
+ *
94
+ * `Rail` is the app shell's side rail — a landmark, an `<aside>` or
95
+ * a `<nav>`, permanent furniture down one edge of a page. This is a
96
+ * horizontal strip of drop targets that exists only for the length
97
+ * of a move, and it is a `listbox`. They share four letters and
98
+ * nothing else. `FileDropZone` sets the precedent for the prefix
99
+ * carrying the distinction.
100
+ *
101
+ * ### What it deliberately does not do
102
+ *
103
+ * It does not render the thing being dragged, own the handle, or
104
+ * know what a move means. The host keeps all three, which is what
105
+ * lets the same rail sit above a board, a card grid, or a list of
106
+ * accordions without any of them being modelled here.
107
+ */
108
+ export declare const DropRail: ({ className, isOpen, label, onDismiss, onPick, targets, }: DropRailProps) => ReactNode;
109
+ //# sourceMappingURL=DropRail.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"DropRail.d.ts","sourceRoot":"","sources":["../../src/DropRail/DropRail.tsx"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AAMtC,MAAM,MAAM,cAAc,GAAG;IAC3B;;;;;OAKG;IACH,KAAK,CAAC,EAAE,MAAM,CAAA;IACd;;;;;;;OAOG;IACH,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB,UAAU,CAAC,EAAE,OAAO,CAAA;IACpB,GAAG,EAAE,MAAM,CAAA;IACX,KAAK,EAAE,MAAM,CAAA;IACb,+DAA+D;IAC/D,IAAI,CAAC,EAAE,SAAS,CAAA;CACjB,CAAA;AAED,MAAM,MAAM,aAAa,GAAG;IAC1B,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB;;;;;OAKG;IACH,MAAM,EAAE,OAAO,CAAA;IACf,8DAA8D;IAC9D,KAAK,EAAE,MAAM,CAAA;IACb,SAAS,CAAC,EAAE,MAAM,IAAI,CAAA;IACtB,MAAM,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,CAAA;IACnC,OAAO,EAAE,SAAS,cAAc,EAAE,CAAA;CACnC,CAAA;AAYD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmEG;AACH,eAAO,MAAM,QAAQ,GAAI,2DAOtB,aAAa,KAAG,SAuWlB,CAAA"}
@@ -0,0 +1,277 @@
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import { useLatestRef, useRovingFocus, useUniqueId, } from "@charcuterie/logic";
3
+ import { useEffect, useRef, useState } from "react";
4
+ import { toClassName } from "../toClassName.js";
5
+ import { VisuallyHidden } from "../VisuallyHidden/VisuallyHidden.js";
6
+ /**
7
+ * The attribute the pointer hit test reads. A `data-` attribute
8
+ * rather than a class, for the same reason `Board` marks its cards
9
+ * with one: a class is a style and may be renamed by a consumer's
10
+ * tooling, and this is a contract with a hit test.
11
+ */
12
+ const TARGET_ATTRIBUTE = "data-drop-rail-target";
13
+ const TARGET_SELECTOR = "[data-drop-rail-target]";
14
+ /**
15
+ * Every destination at once, brought to the pointer instead of the
16
+ * pointer being carried to it.
17
+ *
18
+ * ### The measurement this exists for
19
+ *
20
+ * `Board` moves a card with a `Menu` on the card, and that is the
21
+ * right control for a board: three or four lanes, all on screen, and
22
+ * the menu opens where your hand already is. It stops being the
23
+ * right control at the size the fleet's real lists reach. Docket's
24
+ * Backlog draws **34 project groups down one page**, of which about
25
+ * four fit on a display. Dragging a card from the first to the
26
+ * thirty-fourth means holding a pointer down while the page scrolls,
27
+ * and a menu of 34 items is a scrolling list inside a popup — the
28
+ * same problem in a smaller box.
29
+ *
30
+ * A rail answers both. It pins to the top for the length of the
31
+ * move, so nothing is ever off screen, and there is no travel: every
32
+ * destination is the same short distance away.
33
+ *
34
+ * ### One control, three drivers, and that is the whole design
35
+ *
36
+ * WCAG 2.5.7 requires a single-pointer alternative to any dragging
37
+ * movement, and `FileDropZone` next door says the same thing about
38
+ * its own gesture: build the accessible path first and let the drag
39
+ * be the enhancement. So the rail is **not** a drag affordance with
40
+ * a fallback bolted on. It is a listbox that three things drive:
41
+ *
42
+ * 1. **Pointer.** While a button is held, the chip under the
43
+ * pointer is active and releasing over it commits.
44
+ * 2. **Tap.** Opened with no button held, it is a list you touch.
45
+ * Identical markup, identical commit.
46
+ * 3. **Keyboard.** Arrow keys walk it, Enter commits, Escape
47
+ * abandons. `useRovingFocus` owns which chip is tabbable, so
48
+ * there is no second keyboard model in this file to drift out of
49
+ * step with `Listbox`.
50
+ *
51
+ * The rail does not know which of the three opened it, and the host
52
+ * does not have to say. It reads `event.buttons` on the first
53
+ * pointer event it sees: a button already down means a drag is in
54
+ * flight, and nothing else needs threading through.
55
+ *
56
+ * ### It takes no drag-and-drop dependency, and it hit-tests chips
57
+ *
58
+ * The board's decision priced four libraries and took none of them,
59
+ * because a published library's dependency list is inherited by
60
+ * every consumer — including one with 60 KB gzipped to spend in
61
+ * total. Nothing here changes that arithmetic, and this needs far
62
+ * less than `Board` does: a chip is a box under the pointer, so
63
+ * `elementFromPoint` answers the only geometric question there is.
64
+ * No snapshot, no collision strategy, no sortable transform.
65
+ *
66
+ * ### It is not `Rail`, and the two must not be confused
67
+ *
68
+ * `Rail` is the app shell's side rail — a landmark, an `<aside>` or
69
+ * a `<nav>`, permanent furniture down one edge of a page. This is a
70
+ * horizontal strip of drop targets that exists only for the length
71
+ * of a move, and it is a `listbox`. They share four letters and
72
+ * nothing else. `FileDropZone` sets the precedent for the prefix
73
+ * carrying the distinction.
74
+ *
75
+ * ### What it deliberately does not do
76
+ *
77
+ * It does not render the thing being dragged, own the handle, or
78
+ * know what a move means. The host keeps all three, which is what
79
+ * lets the same rail sit above a board, a card grid, or a list of
80
+ * accordions without any of them being modelled here.
81
+ */
82
+ export const DropRail = ({ className, isOpen, label, onDismiss, onPick, targets, }) => {
83
+ const baseId = useUniqueId();
84
+ const railRef = useRef(null);
85
+ /**
86
+ * A pointer button was already down when the rail opened, so this
87
+ * move is a drag and it commits on release. A tap-opened rail
88
+ * commits on click instead, and the two must not both fire — a
89
+ * released drag also produces a `click`.
90
+ */
91
+ const isPointerDriven = useRef(false);
92
+ /**
93
+ * Focus is moved into the rail once per open, not once per render.
94
+ * Without the latch the effect below re-runs whenever the host
95
+ * re-renders and drags focus back to the first chip, which
96
+ * undoes every arrow key the moment anything above updates.
97
+ */
98
+ const hasTakenFocus = useRef(false);
99
+ const [announcement, setAnnouncement] = useState("");
100
+ const offerableKeys = targets
101
+ .filter((target) => !target.isCurrent && !target.isDisabled)
102
+ .map((target) => target.key);
103
+ /**
104
+ * The offerable keys as one string, because the effects below
105
+ * depend on *which* destinations are offerable and none of them
106
+ * may depend on the array's identity.
107
+ *
108
+ * `targets` is almost always a literal in the host's render, so
109
+ * the array is new on every render. An effect keyed on it
110
+ * re-registers every chip each time, and re-registering loses the
111
+ * active one — which during a keyboard walk means the arrow keys
112
+ * stop moving for no reason a consumer could ever debug.
113
+ */
114
+ const offerableKeysText = offerableKeys.join(" ");
115
+ const firstOfferableKey = offerableKeys[0];
116
+ const { activeValue, next, previous, register, setActiveValue, } = useRovingFocus({ isWrapping: true });
117
+ /**
118
+ * Read by the global listeners, which are registered once per
119
+ * open rather than once per render. Without them the pointer
120
+ * handler closes over a stale `activeValue`, and Enter commits
121
+ * whichever chip was active when the rail opened.
122
+ */
123
+ const activeValueRef = useLatestRef(activeValue);
124
+ const offerableKeysRef = useLatestRef(offerableKeys);
125
+ const onDismissRef = useLatestRef(onDismiss);
126
+ const onPickRef = useLatestRef(onPick);
127
+ const targetsRef = useLatestRef(targets);
128
+ useEffect(() => {
129
+ const releases = offerableKeysText
130
+ .split(" ")
131
+ .filter((key) => key !== "")
132
+ .map((key) => register(key));
133
+ return () => {
134
+ for (const release of releases) {
135
+ release();
136
+ }
137
+ };
138
+ }, [offerableKeysText, register]);
139
+ /**
140
+ * Closing resets everything a reopen must not inherit. Without it
141
+ * a rail reopened for a different card starts on whatever the last
142
+ * move chose, and the first arrow key moves from a place the
143
+ * person never went.
144
+ */
145
+ useEffect(() => {
146
+ if (isOpen) {
147
+ return;
148
+ }
149
+ setActiveValue(null);
150
+ isPointerDriven.current = false;
151
+ hasTakenFocus.current = false;
152
+ setAnnouncement("");
153
+ }, [isOpen, setActiveValue]);
154
+ useEffect(() => {
155
+ if (!isOpen) {
156
+ return undefined;
157
+ }
158
+ const commit = (targetKey) => {
159
+ const target = targetsRef.current.find((one) => one.key === targetKey);
160
+ if (target) {
161
+ setAnnouncement(`Moved to ${target.label}.`);
162
+ }
163
+ onPickRef.current(targetKey);
164
+ };
165
+ const getKeyAt = (clientX, clientY) => {
166
+ const chip = globalThis.document
167
+ .elementFromPoint(clientX, clientY)
168
+ ?.closest(TARGET_SELECTOR);
169
+ return chip?.getAttribute(TARGET_ATTRIBUTE) ?? null;
170
+ };
171
+ const getIsOfferable = (key) => key !== null && offerableKeysRef.current.includes(key);
172
+ const onPointerMove = (pointerEvent) => {
173
+ if (pointerEvent.buttons === 0) {
174
+ return;
175
+ }
176
+ isPointerDriven.current = true;
177
+ const key = getKeyAt(pointerEvent.clientX, pointerEvent.clientY);
178
+ // `null` when the pointer is between chips or off the rail
179
+ // entirely, and that has to clear the highlight. A rail that
180
+ // keeps the last chip lit is telling you it will land
181
+ // somewhere it will not.
182
+ setActiveValue(getIsOfferable(key) ? key : null);
183
+ };
184
+ const onPointerUp = (pointerEvent) => {
185
+ if (!isPointerDriven.current) {
186
+ return;
187
+ }
188
+ const key = getKeyAt(pointerEvent.clientX, pointerEvent.clientY);
189
+ if (getIsOfferable(key) && key !== null) {
190
+ commit(key);
191
+ return;
192
+ }
193
+ onDismissRef.current?.();
194
+ };
195
+ const onKeyDown = (keyEvent) => {
196
+ if (keyEvent.key === "Escape") {
197
+ keyEvent.preventDefault();
198
+ onDismissRef.current?.();
199
+ return;
200
+ }
201
+ if (keyEvent.key === "ArrowRight" ||
202
+ keyEvent.key === "ArrowDown") {
203
+ keyEvent.preventDefault();
204
+ next();
205
+ return;
206
+ }
207
+ if (keyEvent.key === "ArrowLeft" ||
208
+ keyEvent.key === "ArrowUp") {
209
+ keyEvent.preventDefault();
210
+ previous();
211
+ return;
212
+ }
213
+ const isCommitKey = keyEvent.key === "Enter" || keyEvent.key === " ";
214
+ if (isCommitKey && activeValueRef.current !== null) {
215
+ keyEvent.preventDefault();
216
+ commit(activeValueRef.current);
217
+ }
218
+ };
219
+ globalThis.addEventListener("pointermove", onPointerMove);
220
+ globalThis.addEventListener("pointerup", onPointerUp);
221
+ globalThis.addEventListener("keydown", onKeyDown);
222
+ return () => {
223
+ globalThis.removeEventListener("pointermove", onPointerMove);
224
+ globalThis.removeEventListener("pointerup", onPointerUp);
225
+ globalThis.removeEventListener("keydown", onKeyDown);
226
+ };
227
+ }, [
228
+ activeValueRef,
229
+ isOpen,
230
+ next,
231
+ offerableKeysRef,
232
+ onDismissRef,
233
+ onPickRef,
234
+ previous,
235
+ setActiveValue,
236
+ targetsRef,
237
+ ]);
238
+ /**
239
+ * Focus enters the rail only when a pointer is NOT driving it.
240
+ * Stealing focus mid-drag scrolls the page to the rail on some
241
+ * browsers, which is the one thing this control exists to avoid.
242
+ */
243
+ useEffect(() => {
244
+ if (!isOpen ||
245
+ isPointerDriven.current ||
246
+ hasTakenFocus.current ||
247
+ firstOfferableKey === undefined) {
248
+ return;
249
+ }
250
+ hasTakenFocus.current = true;
251
+ setActiveValue(firstOfferableKey);
252
+ railRef.current
253
+ ?.querySelector("[data-drop-rail-target]:not([aria-disabled='true'])")
254
+ ?.focus();
255
+ }, [firstOfferableKey, isOpen, setActiveValue]);
256
+ if (!isOpen) {
257
+ return null;
258
+ }
259
+ return (_jsxs(_Fragment, { children: [_jsx("div", { "aria-label": label, className: toClassName("sticky top-0 z-20 flex flex-wrap items-center gap-2 rounded-md border border-border-default bg-surface-overlay p-2 shadow-(--elevation-medium)", className), id: baseId, ref: railRef, role: "listbox", children: targets.map((target) => {
260
+ const isOfferable = !target.isCurrent && !target.isDisabled;
261
+ const isActive = isOfferable && activeValue === target.key;
262
+ return (_jsxs("button", { "aria-disabled": isOfferable ? undefined : true, "aria-selected": isActive, className: toClassName("flex max-w-80 items-center gap-1.5 rounded-full border px-3 py-1 text-sm transition-colors duration-(--duration-fast) ease-standard", isOfferable
263
+ ? "cursor-pointer border-border-default bg-surface-raised text-content-primary hover:bg-surface-sunken"
264
+ : "cursor-not-allowed border-border-subtle bg-surface-sunken text-content-disabled", isActive &&
265
+ "border-intent-accent-border bg-intent-accent-surface text-intent-accent-content outline-solid outline-(length:--focus-ring-width) outline-focus-ring"), "data-drop-rail-target": target.key, onClick: () => {
266
+ // A released drag also fires a click. The pointer path
267
+ // has already committed by then, so this guard is what
268
+ // stops a drag committing twice.
269
+ if (isPointerDriven.current ||
270
+ !isOfferable) {
271
+ return;
272
+ }
273
+ onPick(target.key);
274
+ }, role: "option", tabIndex: isActive ? 0 : -1, type: "button", children: [target.mark === undefined ? null : (_jsx("span", { "aria-hidden": true, children: target.mark })), _jsx("span", { className: "truncate", children: target.label }), target.count === undefined ? null : (_jsx("span", { className: "text-content-secondary text-xs", children: target.count })), target.isCurrent ? (_jsx(VisuallyHidden, { children: "\u2014 where it is now" })) : null] }, target.key));
275
+ }) }), _jsx("output", { "aria-live": "polite", className: "sr-only", children: announcement })] }));
276
+ };
277
+ //# sourceMappingURL=DropRail.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"DropRail.js","sourceRoot":"","sources":["../../src/DropRail/DropRail.tsx"],"names":[],"mappings":"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"}