@charcuterie/ui 3.21.0 → 3.23.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 (49) hide show
  1. package/README.md +22 -0
  2. package/dist/Board/useBoardDrag.d.ts +15 -1
  3. package/dist/Board/useBoardDrag.d.ts.map +1 -1
  4. package/dist/Board/useBoardDrag.js +3 -3
  5. package/dist/Board/useBoardDrag.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/ReorderList/ReorderList.d.ts +131 -0
  23. package/dist/ReorderList/ReorderList.d.ts.map +1 -0
  24. package/dist/ReorderList/ReorderList.js +154 -0
  25. package/dist/ReorderList/ReorderList.js.map +1 -0
  26. package/dist/index.d.ts +7 -0
  27. package/dist/index.d.ts.map +1 -1
  28. package/dist/index.js +4 -0
  29. package/dist/index.js.map +1 -1
  30. package/package.json +1 -1
  31. package/src/Board/useBoardDrag.ts +17 -2
  32. package/src/DropRail/DropRail.mdx +90 -0
  33. package/src/DropRail/DropRail.stories.tsx +328 -0
  34. package/src/DropRail/DropRail.test.tsx +154 -0
  35. package/src/DropRail/DropRail.tsx +496 -0
  36. package/src/NavBar/NavBar.mdx +157 -0
  37. package/src/NavBar/NavBar.stories.tsx +350 -0
  38. package/src/NavBar/NavBar.test.tsx +344 -0
  39. package/src/NavBar/NavBar.tsx +271 -0
  40. package/src/NavBar/NavBarLink.tsx +139 -0
  41. package/src/NavBar/navBarCurrent.test.ts +81 -0
  42. package/src/NavBar/navBarCurrent.ts +64 -0
  43. package/src/ReorderList/ReorderList.mdx +83 -0
  44. package/src/ReorderList/ReorderList.stories.tsx +149 -0
  45. package/src/ReorderList/ReorderList.test.tsx +190 -0
  46. package/src/ReorderList/ReorderList.tsx +331 -0
  47. package/src/icons.storyHelpers.tsx +12 -0
  48. package/src/index.ts +17 -0
  49. package/src/sourceRules.test.ts +37 -1
@@ -0,0 +1,114 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { useVisibility } from "@charcuterie/logic";
3
+ import { Button } from "../Button/Button.js";
4
+ import { IconButton } from "../IconButton/IconButton.js";
5
+ import { usePanelItemSize } from "../Overlay/usePanelItemSize.js";
6
+ import { Popover } from "../Popover/Popover.js";
7
+ import { useToolbarOverflow } from "../Toolbar/useToolbarOverflow.js";
8
+ import { toClassName } from "../toClassName.js";
9
+ import { NavBarLink } from "./NavBarLink.js";
10
+ import { getIsCurrentHref } from "./navBarCurrent.js";
11
+ /**
12
+ * The app's destinations as real links, in one line — folding into a
13
+ * single menu button, whole, when that line does not fit.
14
+ *
15
+ * ## Why this is not `Toolbar`
16
+ *
17
+ * `Toolbar` already solves collapse-into-an-overflow, and this
18
+ * component **reuses its measurement** (`useToolbarOverflow`) rather
19
+ * than growing a second one. What it cannot reuse is the rendering:
20
+ * a `ToolbarAction` is `{ label, onSelect }` and a collapsed one
21
+ * becomes a `MenuItem`, so a nav folded into a `Toolbar` is a row of
22
+ * `<button>`s. Navigation is an `<a href>` — see `NavBarLink`.
23
+ *
24
+ * The overflow is a `Popover` (`role="dialog"`) rather than a `Menu`
25
+ * for the same reason: `role="menu"` permits only the `menuitem`
26
+ * family, and a `menuitem` is something you *do*, not somewhere you
27
+ * *go*.
28
+ *
29
+ * ## It folds WHOLE, and that is the decision
30
+ *
31
+ * `Toolbar` collapses **progressively** — items drop off the end one
32
+ * at a time — because its items are ranked: the two things you do to
33
+ * a running job matter more than the two you do to the deck. A nav
34
+ * is not ranked. Its order is the shape of the product (Docket's
35
+ * reads as the pipeline: Triage → Backlog → Phases → Board →
36
+ * Archives), and a half-collapsed one splits that shape across two
37
+ * places with no rule the reader can learn — "some of the app is up
38
+ * here and the rest is behind a button" is worse than either whole
39
+ * answer. So the bar shows **every** destination or **none**, and
40
+ * the menu, when it opens, is the complete list in the product's own
41
+ * order.
42
+ *
43
+ * ## Measured, not breakpointed
44
+ *
45
+ * There is no `collapseAt` prop and no media query. The bar reads
46
+ * the width its links actually have and folds when their sum stops
47
+ * fitting, so a longer label, a tenth destination, a
48
+ * `data-density="kiosk"` or a reader at 175% browser zoom each move
49
+ * the fold on their own. A hardcoded pixel number is wrong the first
50
+ * time any of those changes — mux-magic collapses at `480px` and
51
+ * plex-channels at `760px`, and neither number has a reason.
52
+ *
53
+ * ⚠️ **The bar must not be sized by its own contents.** It measures
54
+ * its own box to decide what to draw in it, so a container that
55
+ * shrink-wraps makes the answer its own input: put it in a
56
+ * `minmax(0, 1fr)` grid track or a `flex-1 min-w-0` row. The root
57
+ * carries `min-w-0 flex-1` for the flex case; a grid track is the
58
+ * caller's to declare. Same trap `useAdaptiveColumns` documents from
59
+ * the other end.
60
+ *
61
+ * ## Mounted exactly once
62
+ *
63
+ * A destination is in the bar or in the panel, never both. The
64
+ * fleet's habit is to render the whole nav twice and hide one copy
65
+ * with `hidden md:flex` — mux-magic's `PageHeader` across two
66
+ * ~55-line blocks, mail-sifter's `TriageQueue` again — which puts
67
+ * every link in the DOM at every width, so an agent driving the page
68
+ * finds two of each and cannot tell which one a human can see.
69
+ */
70
+ export const NavBar = ({ className, currentHref, isMenuVisible = false, items, label, menuAlign = "start", menuIcon, menuLabel = "Menu", menuPlacement = "bottom-start", size = "sm", }) => {
71
+ const itemKeys = items.map((item) => item.key ?? item.href);
72
+ const { containerRef, trackItem, visibleCount } = useToolbarOverflow({ itemKeys });
73
+ /**
74
+ * All or nothing. `visibleCount` is the progressive answer and
75
+ * this is the only thing read off it: the moment one link stops
76
+ * fitting, every link goes into the menu together.
77
+ *
78
+ * The hook's `trackTrigger` is deliberately not wired up, and the
79
+ * missing measurement costs nothing here. It exists so a
80
+ * progressive bar can ask "how many fit **beside a trigger**",
81
+ * which is a question this bar never asks: either every link fits
82
+ * with no trigger at all, or the row is one trigger and nothing
83
+ * else. `chooseVisibleCount` tests the whole-row case first and
84
+ * with the trigger costed at zero, so that branch is exact.
85
+ */
86
+ const isCollapsed = visibleCount < items.length;
87
+ const { hide, isVisible, toggle } = useVisibility({
88
+ isVisible: isMenuVisible,
89
+ });
90
+ const panelItemSize = usePanelItemSize("lg");
91
+ const renderLink = (item, placement) => (_jsx(NavBarLink, { isCurrent: getIsCurrentHref({
92
+ currentHref,
93
+ href: item.href,
94
+ }), item: item, onNavigate: placement === "panel" ? hide : undefined, placement: placement, size: placement === "bar" ? size : panelItemSize, trackElement: placement === "bar" ? trackItem : undefined }, item.key ?? item.href));
95
+ const trigger = menuIcon === undefined ? (_jsx(Button, { appearance: "ghost", intent: "neutral", onClick: toggle, size: size, children: menuLabel })) : (_jsx(IconButton, { appearance: "ghost", intent: "neutral", label: menuLabel, onClick: toggle, size: size, children: menuIcon }));
96
+ return (_jsx("nav", { "aria-label": label, className: toClassName("flex min-w-0 flex-1 items-center gap-1",
97
+ // Two written-out entries rather than one interpolated
98
+ // string: Tailwind's scanner cannot see a class that
99
+ // exists only at runtime.
100
+ isCollapsed && menuAlign === "end"
101
+ ? "justify-end"
102
+ : "justify-start", className), ref: containerRef, children: isCollapsed ? (_jsx(Popover
103
+ // The default panel is `max-w-xs p-3` — sized for a
104
+ // paragraph of prose. This one holds full-bleed rows that
105
+ // bring their own inset, so the padding shrinks to the
106
+ // gutter around them and the width is left to the list.
107
+ , {
108
+ // The default panel is `max-w-xs p-3` — sized for a
109
+ // paragraph of prose. This one holds full-bleed rows that
110
+ // bring their own inset, so the padding shrinks to the
111
+ // gutter around them and the width is left to the list.
112
+ className: "max-w-none p-1", heading: menuLabel, isVisible: isVisible, onDismiss: hide, placement: menuPlacement, trigger: trigger, children: _jsx("ul", { className: "flex min-w-48 list-none flex-col gap-0.5", children: items.map((item) => (_jsx("li", { children: renderLink(item, "panel") }, item.key ?? item.href))) }) })) : (items.map((item) => renderLink(item, "bar"))) }));
113
+ };
114
+ //# sourceMappingURL=NavBar.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"NavBar.js","sourceRoot":"","sources":["../../src/NavBar/NavBar.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAA;AAKlD,OAAO,EAAE,MAAM,EAAE,MAAM,qBAAsB,CAAA;AAC7C,OAAO,EAAE,UAAU,EAAE,MAAM,6BAA8B,CAAA;AACzD,OAAO,EAAE,gBAAgB,EAAE,MAAM,gCAAgC,CAAA;AACjE,OAAO,EAAE,OAAO,EAAE,MAAM,uBAAwB,CAAA;AAChD,OAAO,EAAE,kBAAkB,EAAE,MAAM,kCAAkC,CAAA;AACrE,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAA;AAC/C,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAkB,CAAA;AAC7C,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAA;AAsErD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0DG;AACH,MAAM,CAAC,MAAM,MAAM,GAAG,CAAC,EACrB,SAAS,EACT,WAAW,EACX,aAAa,GAAG,KAAK,EACrB,KAAK,EACL,KAAK,EACL,SAAS,GAAG,OAAO,EACnB,QAAQ,EACR,SAAS,GAAG,MAAM,EAClB,aAAa,GAAG,cAAc,EAC9B,IAAI,GAAG,IAAI,GACC,EAAa,EAAE;IAC3B,MAAM,QAAQ,GAAG,KAAK,CAAC,GAAG,CACxB,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,GAAG,IAAI,IAAI,CAAC,IAAI,CAChC,CAAA;IAED,MAAM,EAAE,YAAY,EAAE,SAAS,EAAE,YAAY,EAAE,GAC7C,kBAAkB,CAAC,EAAE,QAAQ,EAAE,CAAC,CAAA;IAElC;;;;;;;;;;;;OAYG;IACH,MAAM,WAAW,GAAG,YAAY,GAAG,KAAK,CAAC,MAAM,CAAA;IAE/C,MAAM,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,EAAE,GAAG,aAAa,CAAC;QAChD,SAAS,EAAE,aAAa;KACzB,CAAC,CAAA;IAEF,MAAM,aAAa,GAAG,gBAAgB,CAAC,IAAI,CAAC,CAAA;IAE5C,MAAM,UAAU,GAAG,CACjB,IAAgB,EAChB,SAA0B,EAC1B,EAAE,CAAC,CACH,KAAC,UAAU,IACT,SAAS,EAAE,gBAAgB,CAAC;YAC1B,WAAW;YACX,IAAI,EAAE,IAAI,CAAC,IAAI;SAChB,CAAC,EACF,IAAI,EAAE,IAAI,EAEV,UAAU,EAAE,SAAS,KAAK,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS,EACpD,SAAS,EAAE,SAAS,EACpB,IAAI,EAAE,SAAS,KAAK,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,aAAa,EAChD,YAAY,EACV,SAAS,KAAK,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,SAAS,IALxC,IAAI,CAAC,GAAG,IAAI,IAAI,CAAC,IAAI,CAO1B,CACH,CAAA;IAED,MAAM,OAAO,GACX,QAAQ,KAAK,SAAS,CAAC,CAAC,CAAC,CACvB,KAAC,MAAM,IACL,UAAU,EAAC,OAAO,EAClB,MAAM,EAAC,SAAS,EAChB,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,IAAI,YAET,SAAS,GACH,CACV,CAAC,CAAC,CAAC,CACF,KAAC,UAAU,IACT,UAAU,EAAC,OAAO,EAClB,MAAM,EAAC,SAAS,EAChB,KAAK,EAAE,SAAS,EAChB,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,IAAI,YAET,QAAQ,GACE,CACd,CAAA;IAEH,OAAO,CACL,4BACc,KAAK,EACjB,SAAS,EAAE,WAAW,CACpB,wCAAwC;QACxC,uDAAuD;QACvD,qDAAqD;QACrD,0BAA0B;QAC1B,WAAW,IAAI,SAAS,KAAK,KAAK;YAChC,CAAC,CAAC,aAAa;YACf,CAAC,CAAC,eAAe,EACnB,SAAS,CACV,EACD,GAAG,EAAE,YAAY,YAEhB,WAAW,CAAC,CAAC,CAAC,CACb,KAAC,OAAO;QACN,oDAAoD;QACpD,0DAA0D;QAC1D,uDAAuD;QACvD,wDAAwD;;YAHxD,oDAAoD;YACpD,0DAA0D;YAC1D,uDAAuD;YACvD,wDAAwD;YACxD,SAAS,EAAC,gBAAgB,EAC1B,OAAO,EAAE,SAAS,EAClB,SAAS,EAAE,SAAS,EACpB,SAAS,EAAE,IAAI,EACf,SAAS,EAAE,aAAa,EACxB,OAAO,EAAE,OAAO,YAQhB,aAAI,SAAS,EAAC,0CAA0C,YACrD,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CACnB,uBACG,UAAU,CAAC,IAAI,EAAE,OAAO,CAAC,IADnB,IAAI,CAAC,GAAG,IAAI,IAAI,CAAC,IAAI,CAEzB,CACN,CAAC,GACC,GACG,CACX,CAAC,CAAC,CAAC,CACF,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,UAAU,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC,CAC7C,GACG,CACP,CAAA;AACH,CAAC,CAAA"}
@@ -0,0 +1,43 @@
1
+ import type { ControlSize } from "@charcuterie/tokens";
2
+ import type { ReactNode } from "react";
3
+ import type { NavBarItem } from "./NavBar.tsx";
4
+ export type NavBarLinkProps = {
5
+ isCurrent: boolean;
6
+ item: NavBarItem;
7
+ /**
8
+ * Fires when the reader activates this link **in a way that keeps
9
+ * them on the page** — a plain left click, Enter, Space. It is
10
+ * what closes the collapsed panel behind them.
11
+ *
12
+ * A modified click (ctrl, meta, shift, alt) opens a new tab and
13
+ * leaves this document exactly where it was, so it deliberately
14
+ * does not fire: shutting the menu the reader is still working
15
+ * through is the behaviour that makes people stop ctrl-clicking.
16
+ */
17
+ onNavigate?: () => void;
18
+ /** Where it is drawn. A bar row and a panel row paint differently. */
19
+ placement: "bar" | "panel";
20
+ size: ControlSize;
21
+ /** Hands the bar row's box to the measuring hook. Bar only. */
22
+ trackElement?: (key: string, element: HTMLElement | null) => void;
23
+ };
24
+ /**
25
+ * One destination, drawn either in the bar or in the collapsed
26
+ * panel — and it is a real `<a href>` in both.
27
+ *
28
+ * That is the whole reason this component exists rather than the
29
+ * `Menu` a collapsed `Toolbar` opens: `MenuItem` is `{ label,
30
+ * onSelect }`, so a nav folded into one becomes a row of
31
+ * `<button>`s. It looks identical in a screenshot and it loses
32
+ * middle-click, ctrl-click, "open in a new tab", "copy link
33
+ * address", the status bar, and the browser's own visited styling.
34
+ * `BuildingAnApp.mdx` names `<button onClick={navigate}>` as the
35
+ * fleet's most common navigation defect; a collapse mechanism that
36
+ * introduced it at narrow widths would be that defect on a timer.
37
+ *
38
+ * The router seam is `ButtonLink`'s, unchanged: an external
39
+ * destination or a fragment falls back to a plain anchor rather than
40
+ * being pushed onto a history stack that has no route for it.
41
+ */
42
+ export declare const NavBarLink: ({ isCurrent, item, onNavigate, placement, size, trackElement, }: NavBarLinkProps) => ReactNode;
43
+ //# sourceMappingURL=NavBarLink.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"NavBarLink.d.ts","sourceRoot":"","sources":["../../src/NavBar/NavBarLink.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAA;AACtD,OAAO,KAAK,EAAc,SAAS,EAAE,MAAM,OAAO,CAAA;AAUlD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,cAAc,CAAA;AAE9C,MAAM,MAAM,eAAe,GAAG;IAC5B,SAAS,EAAE,OAAO,CAAA;IAClB,IAAI,EAAE,UAAU,CAAA;IAChB;;;;;;;;;OASG;IACH,UAAU,CAAC,EAAE,MAAM,IAAI,CAAA;IACvB,sEAAsE;IACtE,SAAS,EAAE,KAAK,GAAG,OAAO,CAAA;IAC1B,IAAI,EAAE,WAAW,CAAA;IACjB,+DAA+D;IAC/D,YAAY,CAAC,EAAE,CACb,GAAG,EAAE,MAAM,EACX,OAAO,EAAE,WAAW,GAAG,IAAI,KACxB,IAAI,CAAA;CACV,CAAA;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,eAAO,MAAM,UAAU,GAAI,iEAOxB,eAAe,KAAG,SA4EpB,CAAA"}
@@ -0,0 +1,67 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { CONTROL_SIZE_CLASS, PANEL_ITEM_SIZE_CLASS, } from "../controlStyles.js";
3
+ import { FOCUS_RING_CLASS } from "../intentStyles.js";
4
+ import { useRouterLink } from "../RouterLink/RouterLinkProvider.js";
5
+ import { getIsRoutedHref } from "../RouterLink/routerLink.js";
6
+ import { toClassName } from "../toClassName.js";
7
+ /**
8
+ * One destination, drawn either in the bar or in the collapsed
9
+ * panel — and it is a real `<a href>` in both.
10
+ *
11
+ * That is the whole reason this component exists rather than the
12
+ * `Menu` a collapsed `Toolbar` opens: `MenuItem` is `{ label,
13
+ * onSelect }`, so a nav folded into one becomes a row of
14
+ * `<button>`s. It looks identical in a screenshot and it loses
15
+ * middle-click, ctrl-click, "open in a new tab", "copy link
16
+ * address", the status bar, and the browser's own visited styling.
17
+ * `BuildingAnApp.mdx` names `<button onClick={navigate}>` as the
18
+ * fleet's most common navigation defect; a collapse mechanism that
19
+ * introduced it at narrow widths would be that defect on a timer.
20
+ *
21
+ * The router seam is `ButtonLink`'s, unchanged: an external
22
+ * destination or a fragment falls back to a plain anchor rather than
23
+ * being pushed onto a history stack that has no route for it.
24
+ */
25
+ export const NavBarLink = ({ isCurrent, item, onNavigate, placement, size, trackElement, }) => {
26
+ const RouterLink = useRouterLink();
27
+ const LinkElement = getIsRoutedHref(item.href)
28
+ ? RouterLink
29
+ : "a";
30
+ const className = placement === "bar"
31
+ ? toClassName("inline-flex shrink-0 items-center rounded-md border border-transparent whitespace-nowrap transition-colors duration-(--duration-fast) ease-standard", CONTROL_SIZE_CLASS[size],
32
+ // Two written-out entries rather than one interpolated
33
+ // string, the same rule `SegmentedOption` follows:
34
+ // Tailwind's scanner cannot see a class that exists only
35
+ // at runtime, and `tailwindCandidates.test.ts` rejects a
36
+ // template literal in a className outright.
37
+ isCurrent
38
+ ? "bg-intent-neutral-surface font-medium text-content-primary"
39
+ : "text-content-secondary hover:bg-intent-neutral-surface hover:text-content-primary", FOCUS_RING_CLASS)
40
+ : toClassName("flex w-full items-center rounded-sm text-start transition-colors duration-(--duration-fast) ease-standard", PANEL_ITEM_SIZE_CLASS[size],
41
+ // `-hover`, not the plain surface: the panel is drawn on
42
+ // `surface-overlay`, which every dark scheme paints
43
+ // *lighter* than the base tint, so the plain one reads as
44
+ // no change at all. Same correction `MenuAction` carries.
45
+ isCurrent
46
+ ? "bg-intent-neutral-surface-hover font-medium text-content-primary"
47
+ : "text-content-primary hover:bg-intent-neutral-surface-hover", FOCUS_RING_CLASS);
48
+ return (_jsxs(LinkElement
49
+ // The platform's own "you are here", and the reason the paint
50
+ // above is never the only signal: a highlight is invisible to
51
+ // a screen reader and to anyone who cannot tell the two
52
+ // surfaces apart.
53
+ , { "aria-current": isCurrent ? "page" : undefined, className: className, href: item.href, onClick: (event) => {
54
+ if (event.defaultPrevented ||
55
+ event.button !== 0 ||
56
+ event.altKey ||
57
+ event.ctrlKey ||
58
+ event.metaKey ||
59
+ event.shiftKey) {
60
+ return;
61
+ }
62
+ onNavigate?.();
63
+ }, ref: (element) => {
64
+ trackElement?.(item.key ?? item.href, element);
65
+ }, children: [item.icon === undefined ? null : (_jsx("span", { "aria-hidden": "true", className: "shrink-0 text-content-secondary", children: item.icon })), item.label] }));
66
+ };
67
+ //# sourceMappingURL=NavBarLink.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"NavBarLink.js","sourceRoot":"","sources":["../../src/NavBar/NavBarLink.tsx"],"names":[],"mappings":";AAGA,OAAO,EACL,kBAAkB,EAClB,qBAAqB,GACtB,MAAM,qBAAqB,CAAA;AAC5B,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAA;AACrD,OAAO,EAAE,aAAa,EAAE,MAAM,qCAAsC,CAAA;AACpE,OAAO,EAAE,eAAe,EAAE,MAAM,6BAA6B,CAAA;AAC7D,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAA;AA2B/C;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,CAAC,MAAM,UAAU,GAAG,CAAC,EACzB,SAAS,EACT,IAAI,EACJ,UAAU,EACV,SAAS,EACT,IAAI,EACJ,YAAY,GACI,EAAa,EAAE;IAC/B,MAAM,UAAU,GAAG,aAAa,EAAE,CAAA;IAElC,MAAM,WAAW,GAAG,eAAe,CAAC,IAAI,CAAC,IAAI,CAAC;QAC5C,CAAC,CAAC,UAAU;QACZ,CAAC,CAAC,GAAG,CAAA;IAEP,MAAM,SAAS,GACb,SAAS,KAAK,KAAK;QACjB,CAAC,CAAC,WAAW,CACT,qJAAqJ,EACrJ,kBAAkB,CAAC,IAAI,CAAC;QACxB,uDAAuD;QACvD,mDAAmD;QACnD,yDAAyD;QACzD,yDAAyD;QACzD,4CAA4C;QAC5C,SAAS;YACP,CAAC,CAAC,4DAA4D;YAC9D,CAAC,CAAC,mFAAmF,EACvF,gBAAgB,CACjB;QACH,CAAC,CAAC,WAAW,CACT,2GAA2G,EAC3G,qBAAqB,CAAC,IAAI,CAAC;QAC3B,yDAAyD;QACzD,oDAAoD;QACpD,0DAA0D;QAC1D,0DAA0D;QAC1D,SAAS;YACP,CAAC,CAAC,kEAAkE;YACpE,CAAC,CAAC,4DAA4D,EAChE,gBAAgB,CACjB,CAAA;IAEP,OAAO,CACL,MAAC,WAAW;IACV,8DAA8D;IAC9D,8DAA8D;IAC9D,wDAAwD;IACxD,kBAAkB;wBACJ,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS,EAC5C,SAAS,EAAE,SAAS,EACpB,IAAI,EAAE,IAAI,CAAC,IAAI,EACf,OAAO,EAAE,CAAC,KAAoC,EAAE,EAAE;YAChD,IACE,KAAK,CAAC,gBAAgB;gBACtB,KAAK,CAAC,MAAM,KAAK,CAAC;gBAClB,KAAK,CAAC,MAAM;gBACZ,KAAK,CAAC,OAAO;gBACb,KAAK,CAAC,OAAO;gBACb,KAAK,CAAC,QAAQ,EACd,CAAC;gBACD,OAAM;YACR,CAAC;YAED,UAAU,EAAE,EAAE,CAAA;QAChB,CAAC,EACD,GAAG,EAAE,CAAC,OAAiC,EAAE,EAAE;YACzC,YAAY,EAAE,CAAC,IAAI,CAAC,GAAG,IAAI,IAAI,CAAC,IAAI,EAAE,OAAO,CAAC,CAAA;QAChD,CAAC,aAEA,IAAI,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAGhC,8BACc,MAAM,EAClB,SAAS,EAAC,iCAAiC,YAE1C,IAAI,CAAC,IAAI,GACL,CACR,EAEA,IAAI,CAAC,KAAK,IACC,CACf,CAAA;AACH,CAAC,CAAA"}
@@ -0,0 +1,37 @@
1
+ /**
2
+ * Which destination the reader is on — the rule behind
3
+ * `aria-current="page"`, kept pure so it can be checked in Node.
4
+ *
5
+ * The library is router-agnostic (see `RouterLinkProvider`), so it
6
+ * cannot ask a router which link is active. The app hands it one
7
+ * string — `useLocation().pathname`, `window.location.pathname`,
8
+ * whatever it has — and this decides the rest.
9
+ *
10
+ * ## A parent path is current for its children
11
+ *
12
+ * `/tasks` is current while the reader is on `/tasks/41`, because a
13
+ * nav item names a **section** and a section does not stop being the
14
+ * one you are in when you open something inside it. The match is on
15
+ * whole segments: `/task` is *not* current for `/tasks`, which a bare
16
+ * `startsWith` would get wrong.
17
+ *
18
+ * ## `/` is exact, and that is deliberately not react-router
19
+ *
20
+ * react-router's `<NavLink to="/">` is active on **every** route
21
+ * unless the caller remembers `end`, which is the footgun behind
22
+ * every "why is Home always highlighted" bug in the fleet. The root
23
+ * matches only itself here, and there is no prop to change it: a nav
24
+ * with two current items is a nav that has stopped saying anything.
25
+ *
26
+ * ## The query string and the fragment are not part of the answer
27
+ *
28
+ * `/backlog?focus=search` is still the Backlog. Sorting a list does
29
+ * not move the reader to a different destination.
30
+ */
31
+ export declare const getIsCurrentHref: ({ currentHref, href, }: {
32
+ /** Where the reader is. `undefined` means "nothing is current". */
33
+ currentHref: string | undefined;
34
+ /** Where this destination goes. */
35
+ href: string;
36
+ }) => boolean;
37
+ //# sourceMappingURL=navBarCurrent.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"navBarCurrent.d.ts","sourceRoot":"","sources":["../../src/NavBar/navBarCurrent.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AAUH,eAAO,MAAM,gBAAgB,GAAI,wBAG9B;IACD,mEAAmE;IACnE,WAAW,EAAE,MAAM,GAAG,SAAS,CAAA;IAC/B,mCAAmC;IACnC,IAAI,EAAE,MAAM,CAAA;CACb,KAAG,OAgBH,CAAA"}
@@ -0,0 +1,48 @@
1
+ /**
2
+ * Which destination the reader is on — the rule behind
3
+ * `aria-current="page"`, kept pure so it can be checked in Node.
4
+ *
5
+ * The library is router-agnostic (see `RouterLinkProvider`), so it
6
+ * cannot ask a router which link is active. The app hands it one
7
+ * string — `useLocation().pathname`, `window.location.pathname`,
8
+ * whatever it has — and this decides the rest.
9
+ *
10
+ * ## A parent path is current for its children
11
+ *
12
+ * `/tasks` is current while the reader is on `/tasks/41`, because a
13
+ * nav item names a **section** and a section does not stop being the
14
+ * one you are in when you open something inside it. The match is on
15
+ * whole segments: `/task` is *not* current for `/tasks`, which a bare
16
+ * `startsWith` would get wrong.
17
+ *
18
+ * ## `/` is exact, and that is deliberately not react-router
19
+ *
20
+ * react-router's `<NavLink to="/">` is active on **every** route
21
+ * unless the caller remembers `end`, which is the footgun behind
22
+ * every "why is Home always highlighted" bug in the fleet. The root
23
+ * matches only itself here, and there is no prop to change it: a nav
24
+ * with two current items is a nav that has stopped saying anything.
25
+ *
26
+ * ## The query string and the fragment are not part of the answer
27
+ *
28
+ * `/backlog?focus=search` is still the Backlog. Sorting a list does
29
+ * not move the reader to a different destination.
30
+ */
31
+ const toComparablePath = (href) => {
32
+ const path = href.split(/[#?]/u)[0] ?? "";
33
+ // A trailing slash is the same place, so it must not change the
34
+ // answer — but `/` itself has to survive being trimmed.
35
+ return path.length > 1 ? path.replace(/\/+$/u, "") : path;
36
+ };
37
+ export const getIsCurrentHref = ({ currentHref, href, }) => {
38
+ if (currentHref === undefined) {
39
+ return false;
40
+ }
41
+ const current = toComparablePath(currentHref);
42
+ const target = toComparablePath(href);
43
+ if (target === "" || target === "/") {
44
+ return current === target;
45
+ }
46
+ return (current === target || current.startsWith(`${target}/`));
47
+ };
48
+ //# sourceMappingURL=navBarCurrent.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"navBarCurrent.js","sourceRoot":"","sources":["../../src/NavBar/navBarCurrent.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AAEH,MAAM,gBAAgB,GAAG,CAAC,IAAY,EAAU,EAAE;IAChD,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAA;IAEzC,gEAAgE;IAChE,wDAAwD;IACxD,OAAO,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,CAAA;AAC3D,CAAC,CAAA;AAED,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAAC,EAC/B,WAAW,EACX,IAAI,GAML,EAAW,EAAE;IACZ,IAAI,WAAW,KAAK,SAAS,EAAE,CAAC;QAC9B,OAAO,KAAK,CAAA;IACd,CAAC;IAED,MAAM,OAAO,GAAG,gBAAgB,CAAC,WAAW,CAAC,CAAA;IAE7C,MAAM,MAAM,GAAG,gBAAgB,CAAC,IAAI,CAAC,CAAA;IAErC,IAAI,MAAM,KAAK,EAAE,IAAI,MAAM,KAAK,GAAG,EAAE,CAAC;QACpC,OAAO,OAAO,KAAK,MAAM,CAAA;IAC3B,CAAC;IAED,OAAO,CACL,OAAO,KAAK,MAAM,IAAI,OAAO,CAAC,UAAU,CAAC,GAAG,MAAM,GAAG,CAAC,CACvD,CAAA;AACH,CAAC,CAAA"}
@@ -0,0 +1,131 @@
1
+ import type { ReactNode, Ref } from "react";
2
+ export type ReorderListItem = {
3
+ key: string;
4
+ /**
5
+ * Plain text, and it is not optional. Every accessible name the
6
+ * component writes is built from it — "Move <label> earlier" —
7
+ * and so is the sentence the live region reads after a move. A
8
+ * button announced as "↑" names nothing.
9
+ */
10
+ label: string;
11
+ };
12
+ export type ReorderListRenderArguments<TItem extends ReorderListItem> = {
13
+ /**
14
+ * Spread onto whatever the host wants the drag to start FROM.
15
+ * Absent — as an empty object — when the list is too short to
16
+ * reorder, so a one-row list has no dead handle on it.
17
+ *
18
+ * It is a **handle**, never the whole row: a row holds links,
19
+ * and a link is draggable already.
20
+ */
21
+ gripProps: {
22
+ onPointerDown?: (event: {
23
+ clientX: number;
24
+ clientY: number;
25
+ currentTarget: EventTarget & Element;
26
+ pointerId: number;
27
+ }) => void;
28
+ style?: {
29
+ touchAction: "none";
30
+ };
31
+ title?: string;
32
+ };
33
+ index: number;
34
+ isDragged: boolean;
35
+ isFirst: boolean;
36
+ isLast: boolean;
37
+ item: TItem;
38
+ /**
39
+ * The KEYBOARD path, and it is the primary one. The host draws
40
+ * the two buttons because only the host knows where they sit in
41
+ * its own row; the component supplies what they do and whether
42
+ * each end is disabled.
43
+ */
44
+ moveBy: (offset: -1 | 1) => void;
45
+ /** 1-based, for printing. */
46
+ position: number;
47
+ };
48
+ export type ReorderListProps<TItem extends ReorderListItem> = {
49
+ className?: string;
50
+ /**
51
+ * The container element. `ul` by default, because an ordered
52
+ * pile of rows is a list; `div` for a host whose own CSS grid
53
+ * owns the shape and would have to undo list semantics to get
54
+ * it.
55
+ */
56
+ elementType?: "div" | "ol" | "ul";
57
+ /**
58
+ * The container element, for a host that needs it too.
59
+ *
60
+ * The component keeps its own hold on that element — the drag
61
+ * measures its rows off it — so this is MERGED rather than
62
+ * forwarded. Docket plays a FLIP animation off the same
63
+ * element, and without this the two uses would have to fight
64
+ * over one `ref`.
65
+ */
66
+ elementRef?: Ref<HTMLElement>;
67
+ itemClassName?: string;
68
+ items: readonly TItem[];
69
+ /** The list's accessible name. */
70
+ label: string;
71
+ /**
72
+ * `fromIndex` and `toIndex` are both indexes into `items`, and
73
+ * `toIndex` is already corrected for the row's own removal — so
74
+ * a handler is `splice(from, 1)` then `splice(to, 0, row)` with
75
+ * no arithmetic of its own.
76
+ */
77
+ onReorder: (fromIndex: number, toIndex: number) => void;
78
+ renderItem: (renderArguments: ReorderListRenderArguments<TItem>) => ReactNode;
79
+ };
80
+ /**
81
+ * A vertical list whose rows can be put in a different order — by
82
+ * two buttons, or by dragging a handle.
83
+ *
84
+ * ### Why it is not a fourth hand-rolled reorder
85
+ *
86
+ * Docket wrote this twice: a subtask checklist on HTML5
87
+ * `draggable`, and a phase queue with the buttons and no drag at
88
+ * all. `Board` wrote a third, properly, on Pointer Events. Three
89
+ * implementations of one shape is the case the workspace's
90
+ * component rule exists for, so the geometry here is **`Board`'s
91
+ * own** — `useBoardDrag` with a single registered lane and the
92
+ * item selector pointed at this component's rows. Nothing about
93
+ * the measuring, the snapshot, the threshold or the pointer
94
+ * capture is written twice.
95
+ *
96
+ * ### The buttons are primary, the drag is the enhancement
97
+ *
98
+ * WCAG 2.5.7 requires a single-pointer alternative to any dragging
99
+ * movement, and the board's decision already argues the stronger
100
+ * version: the buttons are not a fallback bolted onto a drag, they
101
+ * are the only path that works from a keyboard, under a screen
102
+ * reader, or under a thumb in the Narrow View. So `renderItem`
103
+ * receives `moveBy` before it receives `gripProps`, and a host
104
+ * that draws no handle at all is a supported host.
105
+ *
106
+ * ### It takes no drag-and-drop dependency
107
+ *
108
+ * The board priced four libraries at 7–32 KB gzipped and took none
109
+ * of them, because a published library's dependency list is
110
+ * inherited by every consumer
111
+ * ([decision](../../../docs/decisions/2026-08-19-the-board-owns-the-move-and-takes-no-drag-and-drop-dependency.md)).
112
+ * This adds nothing to that arithmetic — it is the same 1.4 KB,
113
+ * called twice.
114
+ *
115
+ * ### `draggable="true"` is the instinct this replaces
116
+ *
117
+ * Native HTML5 drag-and-drop **does not fire on touch at all**, so
118
+ * the checklist Docket shipped on it cannot be reordered on the
119
+ * tablet its owner reads it on. Pointer Events are one code path
120
+ * for mouse, touch and pen, and `setPointerCapture` keeps the drag
121
+ * alive once the row has moved out from under the finger.
122
+ *
123
+ * ### What it deliberately does not do
124
+ *
125
+ * It renders no row, no handle, no buttons and no glyph. The host
126
+ * renders all four through `renderItem`, which is what lets one
127
+ * component serve a checklist, a queue and a card list whose CSS
128
+ * grids have nothing in common.
129
+ */
130
+ export declare const ReorderList: <TItem extends ReorderListItem>({ className, elementRef, elementType, itemClassName, items, label, onReorder, renderItem, }: ReorderListProps<TItem>) => ReactNode;
131
+ //# sourceMappingURL=ReorderList.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ReorderList.d.ts","sourceRoot":"","sources":["../../src/ReorderList/ReorderList.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,GAAG,EAAE,MAAM,OAAO,CAAA;AA0B3C,MAAM,MAAM,eAAe,GAAG;IAC5B,GAAG,EAAE,MAAM,CAAA;IACX;;;;;OAKG;IACH,KAAK,EAAE,MAAM,CAAA;CACd,CAAA;AAED,MAAM,MAAM,0BAA0B,CACpC,KAAK,SAAS,eAAe,IAC3B;IACF;;;;;;;OAOG;IACH,SAAS,EAAE;QACT,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE;YACtB,OAAO,EAAE,MAAM,CAAA;YACf,OAAO,EAAE,MAAM,CAAA;YACf,aAAa,EAAE,WAAW,GAAG,OAAO,CAAA;YACpC,SAAS,EAAE,MAAM,CAAA;SAClB,KAAK,IAAI,CAAA;QACV,KAAK,CAAC,EAAE;YAAE,WAAW,EAAE,MAAM,CAAA;SAAE,CAAA;QAC/B,KAAK,CAAC,EAAE,MAAM,CAAA;KACf,CAAA;IACD,KAAK,EAAE,MAAM,CAAA;IACb,SAAS,EAAE,OAAO,CAAA;IAClB,OAAO,EAAE,OAAO,CAAA;IAChB,MAAM,EAAE,OAAO,CAAA;IACf,IAAI,EAAE,KAAK,CAAA;IACX;;;;;OAKG;IACH,MAAM,EAAE,CAAC,MAAM,EAAE,CAAC,CAAC,GAAG,CAAC,KAAK,IAAI,CAAA;IAChC,6BAA6B;IAC7B,QAAQ,EAAE,MAAM,CAAA;CACjB,CAAA;AAED,MAAM,MAAM,gBAAgB,CAC1B,KAAK,SAAS,eAAe,IAC3B;IACF,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB;;;;;OAKG;IACH,WAAW,CAAC,EAAE,KAAK,GAAG,IAAI,GAAG,IAAI,CAAA;IACjC;;;;;;;;OAQG;IACH,UAAU,CAAC,EAAE,GAAG,CAAC,WAAW,CAAC,CAAA;IAC7B,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,KAAK,EAAE,SAAS,KAAK,EAAE,CAAA;IACvB,kCAAkC;IAClC,KAAK,EAAE,MAAM,CAAA;IACb;;;;;OAKG;IACH,SAAS,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,KAAK,IAAI,CAAA;IACvD,UAAU,EAAE,CACV,eAAe,EAAE,0BAA0B,CAAC,KAAK,CAAC,KAC/C,SAAS,CAAA;CACf,CAAA;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiDG;AACH,eAAO,MAAM,WAAW,GAAI,KAAK,SAAS,eAAe,EAAE,6FASxD,gBAAgB,CAAC,KAAK,CAAC,KAAG,SAgK5B,CAAA"}
@@ -0,0 +1,154 @@
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import { useCallback, useState } from "react";
3
+ import { getIsMoveMeaningful, toSettledIndex, } from "../Board/boardMove.js";
4
+ import { useBoardDrag } from "../Board/useBoardDrag.js";
5
+ import { toClassName } from "../toClassName.js";
6
+ import { VisuallyHidden } from "../VisuallyHidden/VisuallyHidden.js";
7
+ /**
8
+ * The attribute the drag's hit test reads on a row. A `data-`
9
+ * attribute rather than a class, the same contract `BoardCard` and
10
+ * `DropRail` already write down: a class is a style and a
11
+ * consumer's tooling may rename it.
12
+ */
13
+ const ITEM_ATTRIBUTE = "data-reorder-item";
14
+ /**
15
+ * The single lane this component registers with the board's drag.
16
+ * A reorder list is a board with one lane and no lane picker, so
17
+ * the key is a constant and never leaves this file.
18
+ */
19
+ const LANE_KEY = "reorder-list";
20
+ /**
21
+ * A vertical list whose rows can be put in a different order — by
22
+ * two buttons, or by dragging a handle.
23
+ *
24
+ * ### Why it is not a fourth hand-rolled reorder
25
+ *
26
+ * Docket wrote this twice: a subtask checklist on HTML5
27
+ * `draggable`, and a phase queue with the buttons and no drag at
28
+ * all. `Board` wrote a third, properly, on Pointer Events. Three
29
+ * implementations of one shape is the case the workspace's
30
+ * component rule exists for, so the geometry here is **`Board`'s
31
+ * own** — `useBoardDrag` with a single registered lane and the
32
+ * item selector pointed at this component's rows. Nothing about
33
+ * the measuring, the snapshot, the threshold or the pointer
34
+ * capture is written twice.
35
+ *
36
+ * ### The buttons are primary, the drag is the enhancement
37
+ *
38
+ * WCAG 2.5.7 requires a single-pointer alternative to any dragging
39
+ * movement, and the board's decision already argues the stronger
40
+ * version: the buttons are not a fallback bolted onto a drag, they
41
+ * are the only path that works from a keyboard, under a screen
42
+ * reader, or under a thumb in the Narrow View. So `renderItem`
43
+ * receives `moveBy` before it receives `gripProps`, and a host
44
+ * that draws no handle at all is a supported host.
45
+ *
46
+ * ### It takes no drag-and-drop dependency
47
+ *
48
+ * The board priced four libraries at 7–32 KB gzipped and took none
49
+ * of them, because a published library's dependency list is
50
+ * inherited by every consumer
51
+ * ([decision](../../../docs/decisions/2026-08-19-the-board-owns-the-move-and-takes-no-drag-and-drop-dependency.md)).
52
+ * This adds nothing to that arithmetic — it is the same 1.4 KB,
53
+ * called twice.
54
+ *
55
+ * ### `draggable="true"` is the instinct this replaces
56
+ *
57
+ * Native HTML5 drag-and-drop **does not fire on touch at all**, so
58
+ * the checklist Docket shipped on it cannot be reordered on the
59
+ * tablet its owner reads it on. Pointer Events are one code path
60
+ * for mouse, touch and pen, and `setPointerCapture` keeps the drag
61
+ * alive once the row has moved out from under the finger.
62
+ *
63
+ * ### What it deliberately does not do
64
+ *
65
+ * It renders no row, no handle, no buttons and no glyph. The host
66
+ * renders all four through `renderItem`, which is what lets one
67
+ * component serve a checklist, a queue and a card list whose CSS
68
+ * grids have nothing in common.
69
+ */
70
+ export const ReorderList = ({ className, elementRef, elementType = "ul", itemClassName, items, label, onReorder, renderItem, }) => {
71
+ const [announcement, setAnnouncement] = useState("");
72
+ /**
73
+ * A single row cannot be put anywhere else, so it gets no grip
74
+ * and no buttons. This is checked here rather than in every
75
+ * host, because "hide the control when it can do nothing" is the
76
+ * part each hand-rolled copy forgot.
77
+ */
78
+ const isReorderable = items.length > 1;
79
+ const commit = useCallback((fromIndex, toIndex) => {
80
+ setAnnouncement(`Moved ${items[fromIndex]?.label ?? ""} to position ${String(toIndex + 1)} of ${String(items.length)}.`);
81
+ onReorder(fromIndex, toIndex);
82
+ }, [items, onReorder]);
83
+ const { drag, registerLane, startDrag } = useBoardDrag({
84
+ itemSelector: `[${ITEM_ATTRIBUTE}]`,
85
+ onDrop: (origin, target) => {
86
+ if (!getIsMoveMeaningful({
87
+ fromIndex: origin.fromIndex,
88
+ fromLaneKey: origin.fromLaneKey,
89
+ toIndex: target.index,
90
+ toLaneKey: target.laneKey,
91
+ })) {
92
+ return;
93
+ }
94
+ commit(origin.fromIndex, toSettledIndex({
95
+ fromIndex: origin.fromIndex,
96
+ fromLaneKey: origin.fromLaneKey,
97
+ toIndex: target.index,
98
+ toLaneKey: target.laneKey,
99
+ }));
100
+ },
101
+ });
102
+ const ListElement = elementType;
103
+ const ItemElement = elementType === "div" ? "div" : "li";
104
+ return (_jsxs(_Fragment, { children: [_jsxs(ListElement, { "aria-label": label, className: toClassName("relative", className), ref: (element) => {
105
+ registerLane(LANE_KEY)(element);
106
+ if (typeof elementRef === "function") {
107
+ elementRef(element);
108
+ return;
109
+ }
110
+ if (elementRef) {
111
+ elementRef.current = element;
112
+ }
113
+ }, children: [items.map((item, index) => (_jsx(ItemElement, { className: itemClassName, [ITEM_ATTRIBUTE]: item.key, children: renderItem({
114
+ gripProps: isReorderable
115
+ ? {
116
+ onPointerDown: (pointerEvent) => {
117
+ startDrag(pointerEvent, {
118
+ fromIndex: index,
119
+ fromLaneKey: LANE_KEY,
120
+ itemKey: item.key,
121
+ });
122
+ },
123
+ /*
124
+ * Without it the browser claims the gesture
125
+ * for a scroll and the `pointermove` stream
126
+ * stops one frame in — which reads as "drag
127
+ * works on the desktop and not on the
128
+ * tablet", the exact bug this component
129
+ * exists to end.
130
+ */
131
+ style: { touchAction: "none" },
132
+ title: "Drag to reorder",
133
+ }
134
+ : {},
135
+ index,
136
+ isDragged: drag?.itemKey === item.key,
137
+ isFirst: index === 0,
138
+ isLast: index === items.length - 1,
139
+ item,
140
+ moveBy: (offset) => {
141
+ const toIndex = index + offset;
142
+ if (!isReorderable ||
143
+ toIndex < 0 ||
144
+ toIndex > items.length - 1) {
145
+ return;
146
+ }
147
+ commit(index, toIndex);
148
+ },
149
+ position: index + 1,
150
+ }) }, item.key))), drag?.indicatorOffset == null ? null : (_jsx(ItemElement, { "aria-hidden": "true", className: "pointer-events-none absolute end-0 start-0 h-0.5 rounded-full bg-intent-accent-solid", style: {
151
+ insetBlockStart: `${String(drag.indicatorOffset)}px`,
152
+ } }))] }), _jsx(VisuallyHidden, { "aria-label": `${label} activity`, role: "status", children: announcement })] }));
153
+ };
154
+ //# sourceMappingURL=ReorderList.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ReorderList.js","sourceRoot":"","sources":["../../src/ReorderList/ReorderList.tsx"],"names":[],"mappings":"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"}