@charcuterie/ui 3.0.0 → 3.2.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 +10 -0
- package/dist/Badge/Badge.d.ts +33 -3
- package/dist/Badge/Badge.d.ts.map +1 -1
- package/dist/Badge/Badge.js +20 -2
- package/dist/Badge/Badge.js.map +1 -1
- package/dist/Board/Board.d.ts +119 -0
- package/dist/Board/Board.d.ts.map +1 -0
- package/dist/Board/Board.js +165 -0
- package/dist/Board/Board.js.map +1 -0
- package/dist/Board/BoardCard.d.ts +120 -0
- package/dist/Board/BoardCard.d.ts.map +1 -0
- package/dist/Board/BoardCard.js +77 -0
- package/dist/Board/BoardCard.js.map +1 -0
- package/dist/Board/BoardLaneList.d.ts +103 -0
- package/dist/Board/BoardLaneList.d.ts.map +1 -0
- package/dist/Board/BoardLaneList.js +69 -0
- package/dist/Board/BoardLaneList.js.map +1 -0
- package/dist/Board/boardMove.d.ts +116 -0
- package/dist/Board/boardMove.d.ts.map +1 -0
- package/dist/Board/boardMove.js +116 -0
- package/dist/Board/boardMove.js.map +1 -0
- package/dist/Board/useBoardDrag.d.ts +44 -0
- package/dist/Board/useBoardDrag.d.ts.map +1 -0
- package/dist/Board/useBoardDrag.js +237 -0
- package/dist/Board/useBoardDrag.js.map +1 -0
- package/dist/Checkbox/Checkbox.d.ts.map +1 -1
- package/dist/Checkbox/Checkbox.js +15 -4
- package/dist/Checkbox/Checkbox.js.map +1 -1
- package/dist/DataTable/DataTable.d.ts +159 -0
- package/dist/DataTable/DataTable.d.ts.map +1 -0
- package/dist/DataTable/DataTable.js +197 -0
- package/dist/DataTable/DataTable.js.map +1 -0
- package/dist/DatePicker/DateGrid.d.ts +79 -0
- package/dist/DatePicker/DateGrid.d.ts.map +1 -0
- package/dist/DatePicker/DateGrid.js +220 -0
- package/dist/DatePicker/DateGrid.js.map +1 -0
- package/dist/DatePicker/DatePicker.d.ts +138 -0
- package/dist/DatePicker/DatePicker.d.ts.map +1 -0
- package/dist/DatePicker/DatePicker.js +384 -0
- package/dist/DatePicker/DatePicker.js.map +1 -0
- package/dist/DatePicker/datePresets.d.ts +29 -0
- package/dist/DatePicker/datePresets.d.ts.map +1 -0
- package/dist/DatePicker/datePresets.js +28 -0
- package/dist/DatePicker/datePresets.js.map +1 -0
- package/dist/DatePicker/parseDateInput.d.ts +106 -0
- package/dist/DatePicker/parseDateInput.d.ts.map +1 -0
- package/dist/DatePicker/parseDateInput.js +419 -0
- package/dist/DatePicker/parseDateInput.js.map +1 -0
- package/dist/DatePicker/plainDate.d.ts +167 -0
- package/dist/DatePicker/plainDate.d.ts.map +1 -0
- package/dist/DatePicker/plainDate.js +272 -0
- package/dist/DatePicker/plainDate.js.map +1 -0
- package/dist/Dialog/Dialog.d.ts +12 -2
- package/dist/Dialog/Dialog.d.ts.map +1 -1
- package/dist/Dialog/Dialog.js +2 -2
- package/dist/Dialog/Dialog.js.map +1 -1
- package/dist/MarkdownEditor/MarkdownEditor.d.ts +147 -0
- package/dist/MarkdownEditor/MarkdownEditor.d.ts.map +1 -0
- package/dist/MarkdownEditor/MarkdownEditor.js +492 -0
- package/dist/MarkdownEditor/MarkdownEditor.js.map +1 -0
- package/dist/MarkdownEditor/markdownCommands.d.ts +202 -0
- package/dist/MarkdownEditor/markdownCommands.d.ts.map +1 -0
- package/dist/MarkdownEditor/markdownCommands.js +379 -0
- package/dist/MarkdownEditor/markdownCommands.js.map +1 -0
- package/dist/MarkdownEditor/markdownSpans.d.ts +92 -0
- package/dist/MarkdownEditor/markdownSpans.d.ts.map +1 -0
- package/dist/MarkdownEditor/markdownSpans.js +303 -0
- package/dist/MarkdownEditor/markdownSpans.js.map +1 -0
- package/dist/Modal/Modal.d.ts +6 -2
- package/dist/Modal/Modal.d.ts.map +1 -1
- package/dist/Modal/Modal.js +1 -1
- package/dist/Modal/Modal.js.map +1 -1
- package/dist/Overlay/OverlayPanel.d.ts +13 -2
- package/dist/Overlay/OverlayPanel.d.ts.map +1 -1
- package/dist/Overlay/OverlayPanel.js +5 -1
- package/dist/Overlay/OverlayPanel.js.map +1 -1
- package/dist/categoricalStyles.d.ts +53 -0
- package/dist/categoricalStyles.d.ts.map +1 -0
- package/dist/categoricalStyles.js +195 -0
- package/dist/categoricalStyles.js.map +1 -0
- package/dist/index.d.ts +22 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +11 -0
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
- package/src/Badge/Badge.mdx +46 -0
- package/src/Badge/Badge.stories.tsx +98 -4
- package/src/Badge/Badge.test.tsx +157 -2
- package/src/Badge/Badge.tsx +49 -3
- package/src/Board/Board.mdx +199 -0
- package/src/Board/Board.stories.tsx +707 -0
- package/src/Board/Board.test.tsx +310 -0
- package/src/Board/Board.tsx +333 -0
- package/src/Board/BoardCard.tsx +290 -0
- package/src/Board/BoardLaneList.tsx +275 -0
- package/src/Board/boardMove.test.ts +213 -0
- package/src/Board/boardMove.ts +225 -0
- package/src/Board/useBoardDrag.ts +413 -0
- package/src/Checkbox/Checkbox.tsx +34 -3
- package/src/DataTable/DataTable.mdx +123 -0
- package/src/DataTable/DataTable.stories.tsx +411 -0
- package/src/DataTable/DataTable.test.tsx +386 -0
- package/src/DataTable/DataTable.tsx +497 -0
- package/src/DatePicker/DateGrid.tsx +467 -0
- package/src/DatePicker/DatePicker.mdx +170 -0
- package/src/DatePicker/DatePicker.stories.tsx +268 -0
- package/src/DatePicker/DatePicker.test.tsx +455 -0
- package/src/DatePicker/DatePicker.tsx +852 -0
- package/src/DatePicker/datePresets.ts +34 -0
- package/src/DatePicker/parseDateInput.test.ts +187 -0
- package/src/DatePicker/parseDateInput.ts +629 -0
- package/src/DatePicker/plainDate.test.ts +375 -0
- package/src/DatePicker/plainDate.ts +465 -0
- package/src/Dialog/Dialog.stories.tsx +48 -0
- package/src/Dialog/Dialog.test.tsx +30 -0
- package/src/Dialog/Dialog.tsx +13 -1
- package/src/MarkdownEditor/MarkdownEditor.mdx +189 -0
- package/src/MarkdownEditor/MarkdownEditor.stories.tsx +267 -0
- package/src/MarkdownEditor/MarkdownEditor.test.tsx +627 -0
- package/src/MarkdownEditor/MarkdownEditor.tsx +862 -0
- package/src/MarkdownEditor/markdownCommands.test.ts +384 -0
- package/src/MarkdownEditor/markdownCommands.ts +566 -0
- package/src/MarkdownEditor/markdownSpans.test.ts +217 -0
- package/src/MarkdownEditor/markdownSpans.ts +458 -0
- package/src/Modal/Modal.tsx +7 -1
- package/src/Overlay/OverlayPanel.tsx +17 -1
- package/src/argTypes.storyHelpers.ts +39 -0
- package/src/categoricalStyles.ts +244 -0
- package/src/index.ts +89 -0
- package/src/sourceRules.test.ts +52 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"BoardCard.js","sourceRoot":"","sources":["../../src/Board/BoardCard.tsx"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import type { ReactNode } from "react";
|
|
2
|
+
import type { BoardItem } from "./BoardCard.tsx";
|
|
3
|
+
export type BoardLane = {
|
|
4
|
+
/**
|
|
5
|
+
* Lane-level controls, beside the count — Docket's *"Accept all"*
|
|
6
|
+
* on the review lane. `Button`s, usually.
|
|
7
|
+
*/
|
|
8
|
+
actions?: ReactNode;
|
|
9
|
+
/**
|
|
10
|
+
* Shown instead of the rows when the lane is empty. Defaults to a
|
|
11
|
+
* real `EmptyState`, because *"Real empty states"* was a stated
|
|
12
|
+
* requirement and an empty lane is the one a person sees most.
|
|
13
|
+
*/
|
|
14
|
+
emptyState?: ReactNode;
|
|
15
|
+
/**
|
|
16
|
+
* The **true** size of the lane, when `items` is a truncated page
|
|
17
|
+
* of it. This is what the count badge shows, and the difference
|
|
18
|
+
* between it and `items.length` is what the "+ n more" line says.
|
|
19
|
+
*
|
|
20
|
+
* Defaults to `items.length`, so a consumer that does not truncate
|
|
21
|
+
* says nothing about truncation.
|
|
22
|
+
*/
|
|
23
|
+
itemCount?: number;
|
|
24
|
+
items: readonly BoardItem[];
|
|
25
|
+
key: string;
|
|
26
|
+
label: string;
|
|
27
|
+
/**
|
|
28
|
+
* What "+ n more in this lane" does. With no handler the line is
|
|
29
|
+
* still shown — the honest count matters more than the
|
|
30
|
+
* affordance — but it is text rather than a button.
|
|
31
|
+
*/
|
|
32
|
+
onShowMore?: () => void;
|
|
33
|
+
};
|
|
34
|
+
export type BoardLaneListProps = {
|
|
35
|
+
/**
|
|
36
|
+
* Pixels from the top of the list box, or `null` for no
|
|
37
|
+
* indicator. Absolute rather than a real element between two
|
|
38
|
+
* cards: an indicator that takes up space moves the cards it is
|
|
39
|
+
* measured against, and the resulting feedback loop is why so many
|
|
40
|
+
* hand-rolled boards flicker.
|
|
41
|
+
*/
|
|
42
|
+
dropIndicatorOffset: number | null;
|
|
43
|
+
/**
|
|
44
|
+
* The lane heading's level. The caller's, because only the caller
|
|
45
|
+
* knows what the board sits under — an `<h3>` directly below the
|
|
46
|
+
* page's `<h1>` is an axe `heading-order` failure, and a board is
|
|
47
|
+
* as likely to sit under a section heading as under the page
|
|
48
|
+
* title.
|
|
49
|
+
*/
|
|
50
|
+
headingLevel: 2 | 3 | 4 | 5;
|
|
51
|
+
isVisibleWhenNarrow: boolean;
|
|
52
|
+
lane: BoardLane;
|
|
53
|
+
moveIcon?: ReactNode;
|
|
54
|
+
moveTargets: readonly {
|
|
55
|
+
key: string;
|
|
56
|
+
label: string;
|
|
57
|
+
}[];
|
|
58
|
+
onMoveToLane?: (itemKey: string, toLaneKey: string) => void;
|
|
59
|
+
onStartDrag?: (pointerEvent: {
|
|
60
|
+
clientX: number;
|
|
61
|
+
clientY: number;
|
|
62
|
+
currentTarget: EventTarget & Element;
|
|
63
|
+
pointerId: number;
|
|
64
|
+
}, origin: {
|
|
65
|
+
fromIndex: number;
|
|
66
|
+
itemKey: string;
|
|
67
|
+
}) => void;
|
|
68
|
+
registerListElement: (element: HTMLElement | null) => void;
|
|
69
|
+
};
|
|
70
|
+
/**
|
|
71
|
+
* One lane: a named heading, an honest count, its rows, and what it
|
|
72
|
+
* says when there are none.
|
|
73
|
+
*
|
|
74
|
+
* ### The lane is the container, and that is the whole design
|
|
75
|
+
*
|
|
76
|
+
* `@container` sits on a wrapper inside this section, so everything
|
|
77
|
+
* below it — the panel, its rows — is sized by **the lane**. Three
|
|
78
|
+
* lanes in a 1600px window are ~500px each; one lane in the Narrow
|
|
79
|
+
* View is ~360px; the same lane in a docked sidebar is 280px. All
|
|
80
|
+
* three happen on the same monitor, and no media query can tell
|
|
81
|
+
* them apart.
|
|
82
|
+
*
|
|
83
|
+
* The wrapper is a wrapper because a container query matches
|
|
84
|
+
* **descendants only**. Declaring `@container` on the panel itself
|
|
85
|
+
* would compile, generate real CSS, and silently never fire — so
|
|
86
|
+
* the panel could never take its own frame off at `cq-lg`, which is
|
|
87
|
+
* the one thing the widest layout asks for.
|
|
88
|
+
*
|
|
89
|
+
* This `<section>` is outside all of that: it carries the
|
|
90
|
+
* hidden/shown classes, and those resolve against the **board's**
|
|
91
|
+
* container, one level up.
|
|
92
|
+
*
|
|
93
|
+
* ### Long lanes truncate; they do not scroll
|
|
94
|
+
*
|
|
95
|
+
* *"Long lanes truncate with an honest `+ n more` rather than
|
|
96
|
+
* scrolling forever."* So the lane never grows a scrollbar and never
|
|
97
|
+
* virtualises. `itemCount` is the real total and `items` is whatever
|
|
98
|
+
* the consumer chose to render — which keeps paging, sorting and any
|
|
99
|
+
* windowing on the side that owns the data, and keeps this component
|
|
100
|
+
* from pretending to know how many rows are worth painting.
|
|
101
|
+
*/
|
|
102
|
+
export declare const BoardLaneList: ({ dropIndicatorOffset, headingLevel, isVisibleWhenNarrow, lane, moveIcon, moveTargets, onMoveToLane, onStartDrag, registerListElement, }: BoardLaneListProps) => ReactNode;
|
|
103
|
+
//# sourceMappingURL=BoardLaneList.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"BoardLaneList.d.ts","sourceRoot":"","sources":["../../src/Board/BoardLaneList.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AAMtC,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAA;AAGhD,MAAM,MAAM,SAAS,GAAG;IACtB;;;OAGG;IACH,OAAO,CAAC,EAAE,SAAS,CAAA;IACnB;;;;OAIG;IACH,UAAU,CAAC,EAAE,SAAS,CAAA;IACtB;;;;;;;OAOG;IACH,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,KAAK,EAAE,SAAS,SAAS,EAAE,CAAA;IAC3B,GAAG,EAAE,MAAM,CAAA;IACX,KAAK,EAAE,MAAM,CAAA;IACb;;;;OAIG;IACH,UAAU,CAAC,EAAE,MAAM,IAAI,CAAA;CACxB,CAAA;AAED,MAAM,MAAM,kBAAkB,GAAG;IAC/B;;;;;;OAMG;IACH,mBAAmB,EAAE,MAAM,GAAG,IAAI,CAAA;IAClC;;;;;;OAMG;IACH,YAAY,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,CAAA;IAC3B,mBAAmB,EAAE,OAAO,CAAA;IAC5B,IAAI,EAAE,SAAS,CAAA;IACf,QAAQ,CAAC,EAAE,SAAS,CAAA;IACpB,WAAW,EAAE,SAAS;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,EAAE,CAAA;IACtD,YAAY,CAAC,EAAE,CACb,OAAO,EAAE,MAAM,EACf,SAAS,EAAE,MAAM,KACd,IAAI,CAAA;IACT,WAAW,CAAC,EAAE,CACZ,YAAY,EAAE;QACZ,OAAO,EAAE,MAAM,CAAA;QACf,OAAO,EAAE,MAAM,CAAA;QACf,aAAa,EAAE,WAAW,GAAG,OAAO,CAAA;QACpC,SAAS,EAAE,MAAM,CAAA;KAClB,EACD,MAAM,EAAE;QAAE,SAAS,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,KAC3C,IAAI,CAAA;IACT,mBAAmB,EAAE,CAAC,OAAO,EAAE,WAAW,GAAG,IAAI,KAAK,IAAI,CAAA;CAC3D,CAAA;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,eAAO,MAAM,aAAa,GAAI,0IAU3B,kBAAkB,KAAG,SAyJvB,CAAA"}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useUniqueId } from "@charcuterie/logic";
|
|
3
|
+
import { Badge } from "../Badge/Badge.js";
|
|
4
|
+
import { Button } from "../Button/Button.js";
|
|
5
|
+
import { EmptyState } from "../EmptyState/EmptyState.js";
|
|
6
|
+
import { toClassName } from "../toClassName.js";
|
|
7
|
+
import { BoardCard } from "./BoardCard.js";
|
|
8
|
+
/**
|
|
9
|
+
* One lane: a named heading, an honest count, its rows, and what it
|
|
10
|
+
* says when there are none.
|
|
11
|
+
*
|
|
12
|
+
* ### The lane is the container, and that is the whole design
|
|
13
|
+
*
|
|
14
|
+
* `@container` sits on a wrapper inside this section, so everything
|
|
15
|
+
* below it — the panel, its rows — is sized by **the lane**. Three
|
|
16
|
+
* lanes in a 1600px window are ~500px each; one lane in the Narrow
|
|
17
|
+
* View is ~360px; the same lane in a docked sidebar is 280px. All
|
|
18
|
+
* three happen on the same monitor, and no media query can tell
|
|
19
|
+
* them apart.
|
|
20
|
+
*
|
|
21
|
+
* The wrapper is a wrapper because a container query matches
|
|
22
|
+
* **descendants only**. Declaring `@container` on the panel itself
|
|
23
|
+
* would compile, generate real CSS, and silently never fire — so
|
|
24
|
+
* the panel could never take its own frame off at `cq-lg`, which is
|
|
25
|
+
* the one thing the widest layout asks for.
|
|
26
|
+
*
|
|
27
|
+
* This `<section>` is outside all of that: it carries the
|
|
28
|
+
* hidden/shown classes, and those resolve against the **board's**
|
|
29
|
+
* container, one level up.
|
|
30
|
+
*
|
|
31
|
+
* ### Long lanes truncate; they do not scroll
|
|
32
|
+
*
|
|
33
|
+
* *"Long lanes truncate with an honest `+ n more` rather than
|
|
34
|
+
* scrolling forever."* So the lane never grows a scrollbar and never
|
|
35
|
+
* virtualises. `itemCount` is the real total and `items` is whatever
|
|
36
|
+
* the consumer chose to render — which keeps paging, sorting and any
|
|
37
|
+
* windowing on the side that owns the data, and keeps this component
|
|
38
|
+
* from pretending to know how many rows are worth painting.
|
|
39
|
+
*/
|
|
40
|
+
export const BoardLaneList = ({ dropIndicatorOffset, headingLevel, isVisibleWhenNarrow, lane, moveIcon, moveTargets, onMoveToLane, onStartDrag, registerListElement, }) => {
|
|
41
|
+
const headingId = useUniqueId();
|
|
42
|
+
const itemCount = lane.itemCount ?? lane.items.length;
|
|
43
|
+
const hiddenCount = itemCount - lane.items.length;
|
|
44
|
+
const Heading = `h${headingLevel}`;
|
|
45
|
+
return (_jsx("div", { "aria-labelledby": headingId, role: "group", className: toClassName("min-w-0 flex-1 flex-col gap-2",
|
|
46
|
+
// Below the board's own `cq-lg`, one lane is on screen and
|
|
47
|
+
// the rest are chosen with the segmented control above. This
|
|
48
|
+
// queries the **board**, not the list below — an element
|
|
49
|
+
// carrying `container-type` still resolves its own container
|
|
50
|
+
// queries against its nearest ancestor container.
|
|
51
|
+
isVisibleWhenNarrow ? "flex" : "hidden cq-lg:flex"), children: _jsx("div", { className: "@container", children: _jsxs("div", { className: "flex flex-col gap-2 rounded-lg border border-border-subtle bg-surface-raised px-3 py-2 cq-lg:border-0 cq-lg:bg-transparent cq-lg:p-0", children: [_jsxs("header", { className: "flex items-center gap-2", children: [_jsx(Heading, { className: "font-semibold text-content-primary text-sm", id: headingId, children: lane.label }), _jsx(Badge, { appearance: "soft", intent: "neutral", size: "sm", children: String(itemCount) }), _jsx("span", { className: "flex-1" }), lane.actions] }), _jsxs("ul", { className: "relative flex flex-col", ref: registerListElement, children: [lane.items.length === 0 ? (_jsx("li", { children: lane.emptyState ?? (_jsx(EmptyState, { description: "Nothing is waiting here.", heading: "Nothing here", headingLevel:
|
|
52
|
+
// One below the lane's own, so the outline
|
|
53
|
+
// stays whole wherever the board is mounted.
|
|
54
|
+
(headingLevel + 1), size: "sm" })) })) : (lane.items.map((item, index) => (_jsx(BoardCard, { item: item, laneLabel: lane.label, moveIcon: moveIcon, moveTargets: moveTargets, onMoveToLane: onMoveToLane
|
|
55
|
+
? (toLaneKey) => {
|
|
56
|
+
onMoveToLane(item.key, toLaneKey);
|
|
57
|
+
}
|
|
58
|
+
: undefined, onStartDrag: onStartDrag
|
|
59
|
+
? (pointerEvent) => {
|
|
60
|
+
onStartDrag(pointerEvent, {
|
|
61
|
+
fromIndex: index,
|
|
62
|
+
itemKey: item.key,
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
: undefined }, item.key)))), dropIndicatorOffset === null ? null : (_jsx("li", { "aria-hidden": "true", className: "pointer-events-none absolute end-0 start-0 h-0.5 rounded-full bg-intent-accent-solid", style: {
|
|
66
|
+
insetBlockStart: `${dropIndicatorOffset}px`,
|
|
67
|
+
} }))] }), hiddenCount > 0 ? (_jsx("p", { className: "text-content-muted text-xs", children: lane.onShowMore ? (_jsx(Button, { appearance: "ghost", intent: "neutral", onClick: lane.onShowMore, size: "sm", children: `+ ${hiddenCount} more in ${lane.label}` })) : (`+ ${hiddenCount} more in ${lane.label}`) })) : null] }) }) }));
|
|
68
|
+
};
|
|
69
|
+
//# sourceMappingURL=BoardLaneList.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"BoardLaneList.js","sourceRoot":"","sources":["../../src/Board/BoardLaneList.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAA;AAGhD,OAAO,EAAE,KAAK,EAAE,MAAM,mBAAoB,CAAA;AAC1C,OAAO,EAAE,MAAM,EAAE,MAAM,qBAAsB,CAAA;AAC7C,OAAO,EAAE,UAAU,EAAE,MAAM,6BAA8B,CAAA;AACzD,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAA;AAE/C,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAiB,CAAA;AAuE3C;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,MAAM,CAAC,MAAM,aAAa,GAAG,CAAC,EAC5B,mBAAmB,EACnB,YAAY,EACZ,mBAAmB,EACnB,IAAI,EACJ,QAAQ,EACR,WAAW,EACX,YAAY,EACZ,WAAW,EACX,mBAAmB,GACA,EAAa,EAAE;IAClC,MAAM,SAAS,GAAG,WAAW,EAAE,CAAA;IAE/B,MAAM,SAAS,GAAG,IAAI,CAAC,SAAS,IAAI,IAAI,CAAC,KAAK,CAAC,MAAM,CAAA;IAErD,MAAM,WAAW,GAAG,SAAS,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,CAAA;IAEjD,MAAM,OAAO,GAAG,IAAI,YAAY,EAAW,CAAA;IAE3C,OAAO,CAqBL,iCACmB,SAAS,EAC1B,IAAI,EAAC,OAAO,EACZ,SAAS,EAAE,WAAW,CACpB,+BAA+B;QAC/B,2DAA2D;QAC3D,6DAA6D;QAC7D,yDAAyD;QACzD,6DAA6D;QAC7D,kDAAkD;QAClD,mBAAmB,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,mBAAmB,CACnD,YAUD,cAAK,SAAS,EAAC,YAAY,YACzB,eAAK,SAAS,EAAC,sIAAsI,aACnJ,kBAAQ,SAAS,EAAC,yBAAyB,aACzC,KAAC,OAAO,IACN,SAAS,EAAC,4CAA4C,EACtD,EAAE,EAAE,SAAS,YAEZ,IAAI,CAAC,KAAK,GACH,EAEV,KAAC,KAAK,IACJ,UAAU,EAAC,MAAM,EACjB,MAAM,EAAC,SAAS,EAChB,IAAI,EAAC,IAAI,YAER,MAAM,CAAC,SAAS,CAAC,GACZ,EAER,eAAM,SAAS,EAAC,QAAQ,GAAG,EAE1B,IAAI,CAAC,OAAO,IACN,EAET,cACE,SAAS,EAAC,wBAAwB,EAClC,GAAG,EAAE,mBAAmB,aAEvB,IAAI,CAAC,KAAK,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,CACzB,uBACG,IAAI,CAAC,UAAU,IAAI,CAClB,KAAC,UAAU,IACT,WAAW,EAAC,0BAA0B,EACtC,OAAO,EAAC,cAAc,EACtB,YAAY;oCACV,2CAA2C;oCAC3C,6CAA6C;oCAC7C,CAAC,YAAY,GAAG,CAAC,CAAkB,EAErC,IAAI,EAAC,IAAI,GACT,CACH,GACE,CACN,CAAC,CAAC,CAAC,CACF,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC,CAC9B,KAAC,SAAS,IACR,IAAI,EAAE,IAAI,EAEV,SAAS,EAAE,IAAI,CAAC,KAAK,EACrB,QAAQ,EAAE,QAAQ,EAClB,WAAW,EAAE,WAAW,EACxB,YAAY,EACV,YAAY;oCACV,CAAC,CAAC,CAAC,SAAS,EAAE,EAAE;wCACZ,YAAY,CAAC,IAAI,CAAC,GAAG,EAAE,SAAS,CAAC,CAAA;oCACnC,CAAC;oCACH,CAAC,CAAC,SAAS,EAEf,WAAW,EACT,WAAW;oCACT,CAAC,CAAC,CAAC,YAAY,EAAE,EAAE;wCACf,WAAW,CAAC,YAAY,EAAE;4CACxB,SAAS,EAAE,KAAK;4CAChB,OAAO,EAAE,IAAI,CAAC,GAAG;yCAClB,CAAC,CAAA;oCACJ,CAAC;oCACH,CAAC,CAAC,SAAS,IAnBV,IAAI,CAAC,GAAG,CAqBb,CACH,CAAC,CACH,EAEA,mBAAmB,KAAK,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CACrC,4BACc,MAAM,EAClB,SAAS,EAAC,sFAAsF,EAChG,KAAK,EAAE;oCACL,eAAe,EAAE,GAAG,mBAAmB,IAAI;iCAC5C,GACD,CACH,IACE,EAEJ,WAAW,GAAG,CAAC,CAAC,CAAC,CAAC,CACjB,YAAG,SAAS,EAAC,4BAA4B,YACtC,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,CACjB,KAAC,MAAM,IACL,UAAU,EAAC,OAAO,EAClB,MAAM,EAAC,SAAS,EAChB,OAAO,EAAE,IAAI,CAAC,UAAU,EACxB,IAAI,EAAC,IAAI,YAER,KAAK,WAAW,YAAY,IAAI,CAAC,KAAK,EAAE,GAClC,CACV,CAAC,CAAC,CAAC,CACF,KAAK,WAAW,YAAY,IAAI,CAAC,KAAK,EAAE,CACzC,GACC,CACL,CAAC,CAAC,CAAC,IAAI,IACJ,GACF,GACF,CACP,CAAA;AACH,CAAC,CAAA"}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Everything about a board move that is arithmetic rather than
|
|
3
|
+
* React — so it can be tested in Node, exhaustively, without a
|
|
4
|
+
* browser and without a pointer.
|
|
5
|
+
*
|
|
6
|
+
* That split is not tidiness. The pointer path is the one part of
|
|
7
|
+
* this component a test cannot drive convincingly: `userEvent` can
|
|
8
|
+
* dispatch `pointermove`, but it cannot lay anything out, so every
|
|
9
|
+
* rectangle it would reason about is `0x0` in a test environment
|
|
10
|
+
* that has not painted. Pulling the geometry out means the *rule* —
|
|
11
|
+
* which lane, which index, what happens on a tie — is red/green in
|
|
12
|
+
* Node, and the browser test is left proving only the wiring.
|
|
13
|
+
*/
|
|
14
|
+
/**
|
|
15
|
+
* A lane as the drop calculation sees it: a box, a key, and the
|
|
16
|
+
* boxes of the cards inside it.
|
|
17
|
+
*
|
|
18
|
+
* Deliberately plain data rather than elements. `chooseDropTarget`
|
|
19
|
+
* never touches the DOM, which is why it can be called with
|
|
20
|
+
* fixtures.
|
|
21
|
+
*/
|
|
22
|
+
export type BoardDropLane = {
|
|
23
|
+
cardRects: readonly BoardRect[];
|
|
24
|
+
key: string;
|
|
25
|
+
rect: BoardRect;
|
|
26
|
+
};
|
|
27
|
+
export type BoardPoint = {
|
|
28
|
+
x: number;
|
|
29
|
+
y: number;
|
|
30
|
+
};
|
|
31
|
+
/**
|
|
32
|
+
* The subset of `DOMRect` this needs. Named rather than reusing
|
|
33
|
+
* `DOMRect`, because `DOMRect` is a browser global and this module
|
|
34
|
+
* is imported by the Node test project.
|
|
35
|
+
*/
|
|
36
|
+
export type BoardRect = {
|
|
37
|
+
height: number;
|
|
38
|
+
left: number;
|
|
39
|
+
top: number;
|
|
40
|
+
width: number;
|
|
41
|
+
};
|
|
42
|
+
export type BoardDropTarget = {
|
|
43
|
+
index: number;
|
|
44
|
+
laneKey: string;
|
|
45
|
+
};
|
|
46
|
+
/**
|
|
47
|
+
* Where in a lane a point lands: the number of cards whose midpoint
|
|
48
|
+
* is above it.
|
|
49
|
+
*
|
|
50
|
+
* Midpoints rather than edges, so the insertion flips when the
|
|
51
|
+
* pointer passes the *centre* of a card. Against edges, the top
|
|
52
|
+
* half of every card is a dead zone that means "same place you
|
|
53
|
+
* already were", and the indicator stutters.
|
|
54
|
+
*/
|
|
55
|
+
export declare const chooseDropIndex: (cardRects: readonly BoardRect[], point: BoardPoint) => number;
|
|
56
|
+
/**
|
|
57
|
+
* The lane and index a pointer is currently over.
|
|
58
|
+
*
|
|
59
|
+
* Hit test first, nearest-lane second, and `null` only when there
|
|
60
|
+
* are no lanes at all — a drag that started has to end somewhere.
|
|
61
|
+
*/
|
|
62
|
+
export declare const chooseDropTarget: (lanes: readonly BoardDropLane[], point: BoardPoint) => BoardDropTarget | null;
|
|
63
|
+
/**
|
|
64
|
+
* Whether a move would actually change anything.
|
|
65
|
+
*
|
|
66
|
+
* The subtle half is `toIndex`. Taking a card out of its own lane
|
|
67
|
+
* shifts every card below it up by one, so "drop it back where it
|
|
68
|
+
* was" arrives as both `fromIndex` and `fromIndex + 1` depending on
|
|
69
|
+
* which side of its own midpoint the pointer ended on. Both are
|
|
70
|
+
* no-ops, and a board that fires `onMove` for them makes a consumer
|
|
71
|
+
* re-render — and, if the consumer is optimistic, flash — for a
|
|
72
|
+
* drag that went nowhere.
|
|
73
|
+
*/
|
|
74
|
+
export declare const getIsMoveMeaningful: ({ fromIndex, fromLaneKey, toIndex, toLaneKey, }: {
|
|
75
|
+
fromIndex: number;
|
|
76
|
+
fromLaneKey: string;
|
|
77
|
+
toIndex: number;
|
|
78
|
+
toLaneKey: string;
|
|
79
|
+
}) => boolean;
|
|
80
|
+
/**
|
|
81
|
+
* Where a card ends up once it has been removed from its old
|
|
82
|
+
* position.
|
|
83
|
+
*
|
|
84
|
+
* A within-lane move is quoted against the list *including* the
|
|
85
|
+
* card being moved — that is what the pointer was over — but a
|
|
86
|
+
* consumer splicing an array removes it first, so every index below
|
|
87
|
+
* the origin is one too high. Folding that here means the consumer's
|
|
88
|
+
* handler is `splice(from, 1)` then `splice(to, 0, item)` with no
|
|
89
|
+
* arithmetic of its own, which is the version nobody gets wrong.
|
|
90
|
+
*/
|
|
91
|
+
export declare const toSettledIndex: ({ fromIndex, fromLaneKey, toIndex, toLaneKey, }: {
|
|
92
|
+
fromIndex: number;
|
|
93
|
+
fromLaneKey: string;
|
|
94
|
+
toIndex: number;
|
|
95
|
+
toLaneKey: string;
|
|
96
|
+
}) => number;
|
|
97
|
+
/**
|
|
98
|
+
* What the live region says after a move.
|
|
99
|
+
*
|
|
100
|
+
* A sentence rather than a status word, and it names the
|
|
101
|
+
* **destination position** as well as the lane, because "moved to In
|
|
102
|
+
* Progress" leaves a screen-reader user with no idea whether the
|
|
103
|
+
* card went to the top of the lane or the bottom of thirty. That is
|
|
104
|
+
* the single piece of feedback a sighted user gets for free from
|
|
105
|
+
* watching the card land, and it is the one every drag-and-drop
|
|
106
|
+
* implementation forgets to say out loud.
|
|
107
|
+
*
|
|
108
|
+
* One-based, because it is read to a person.
|
|
109
|
+
*/
|
|
110
|
+
export declare const describeMove: ({ index, laneLabel, laneSize, title, }: {
|
|
111
|
+
index: number;
|
|
112
|
+
laneLabel: string;
|
|
113
|
+
laneSize: number;
|
|
114
|
+
title: string;
|
|
115
|
+
}) => string;
|
|
116
|
+
//# sourceMappingURL=boardMove.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"boardMove.d.ts","sourceRoot":"","sources":["../../src/Board/boardMove.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH;;;;;;;GAOG;AACH,MAAM,MAAM,aAAa,GAAG;IAC1B,SAAS,EAAE,SAAS,SAAS,EAAE,CAAA;IAC/B,GAAG,EAAE,MAAM,CAAA;IACX,IAAI,EAAE,SAAS,CAAA;CAChB,CAAA;AAED,MAAM,MAAM,UAAU,GAAG;IACvB,CAAC,EAAE,MAAM,CAAA;IACT,CAAC,EAAE,MAAM,CAAA;CACV,CAAA;AAED;;;;GAIG;AACH,MAAM,MAAM,SAAS,GAAG;IACtB,MAAM,EAAE,MAAM,CAAA;IACd,IAAI,EAAE,MAAM,CAAA;IACZ,GAAG,EAAE,MAAM,CAAA;IACX,KAAK,EAAE,MAAM,CAAA;CACd,CAAA;AAED,MAAM,MAAM,eAAe,GAAG;IAC5B,KAAK,EAAE,MAAM,CAAA;IACb,OAAO,EAAE,MAAM,CAAA;CAChB,CAAA;AAmDD;;;;;;;;GAQG;AACH,eAAO,MAAM,eAAe,GAC1B,WAAW,SAAS,SAAS,EAAE,EAC/B,OAAO,UAAU,KAChB,MAGO,CAAA;AAEV;;;;;GAKG;AACH,eAAO,MAAM,gBAAgB,GAC3B,OAAO,SAAS,aAAa,EAAE,EAC/B,OAAO,UAAU,KAChB,eAAe,GAAG,IAsBpB,CAAA;AAED;;;;;;;;;;GAUG;AACH,eAAO,MAAM,mBAAmB,GAAI,iDAKjC;IACD,SAAS,EAAE,MAAM,CAAA;IACjB,WAAW,EAAE,MAAM,CAAA;IACnB,OAAO,EAAE,MAAM,CAAA;IACf,SAAS,EAAE,MAAM,CAAA;CAClB,KAAG,OAEkD,CAAA;AAEtD;;;;;;;;;;GAUG;AACH,eAAO,MAAM,cAAc,GAAI,iDAK5B;IACD,SAAS,EAAE,MAAM,CAAA;IACjB,WAAW,EAAE,MAAM,CAAA;IACnB,OAAO,EAAE,MAAM,CAAA;IACf,SAAS,EAAE,MAAM,CAAA;CAClB,KAAG,MAGS,CAAA;AAEb;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,YAAY,GAAI,wCAK1B;IACD,KAAK,EAAE,MAAM,CAAA;IACb,SAAS,EAAE,MAAM,CAAA;IACjB,QAAQ,EAAE,MAAM,CAAA;IAChB,KAAK,EAAE,MAAM,CAAA;CACd,KAAG,MACqE,CAAA"}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Everything about a board move that is arithmetic rather than
|
|
3
|
+
* React — so it can be tested in Node, exhaustively, without a
|
|
4
|
+
* browser and without a pointer.
|
|
5
|
+
*
|
|
6
|
+
* That split is not tidiness. The pointer path is the one part of
|
|
7
|
+
* this component a test cannot drive convincingly: `userEvent` can
|
|
8
|
+
* dispatch `pointermove`, but it cannot lay anything out, so every
|
|
9
|
+
* rectangle it would reason about is `0x0` in a test environment
|
|
10
|
+
* that has not painted. Pulling the geometry out means the *rule* —
|
|
11
|
+
* which lane, which index, what happens on a tie — is red/green in
|
|
12
|
+
* Node, and the browser test is left proving only the wiring.
|
|
13
|
+
*/
|
|
14
|
+
/**
|
|
15
|
+
* `left`/`top` rather than `insetInlineStart`, and that is not a
|
|
16
|
+
* violation of the logical-properties rule.
|
|
17
|
+
*
|
|
18
|
+
* The rule is scoped to `className` literals, because a Tailwind
|
|
19
|
+
* utility is a *style* and a style has a writing direction.
|
|
20
|
+
* `getBoundingClientRect()` returns physical pixels in the
|
|
21
|
+
* viewport's own coordinate space; there is no logical version of it
|
|
22
|
+
* to prefer, and inventing one here would mean re-deriving a number
|
|
23
|
+
* the browser already gave us. The lint rule carves this out
|
|
24
|
+
* explicitly.
|
|
25
|
+
*/
|
|
26
|
+
const getIsPointInside = (rect, point) => point.x >= rect.left &&
|
|
27
|
+
point.x <= rect.left + rect.width &&
|
|
28
|
+
point.y >= rect.top &&
|
|
29
|
+
point.y <= rect.top + rect.height;
|
|
30
|
+
/**
|
|
31
|
+
* How far a point is from a rectangle, zero when it is inside.
|
|
32
|
+
*
|
|
33
|
+
* Used only as a fallback, and the fallback is the reason a drag
|
|
34
|
+
* feels finished rather than cancelled: a pointer released two
|
|
35
|
+
* pixels past a lane's edge is a drop *on that lane* as far as the
|
|
36
|
+
* person doing it is concerned, and returning `null` there is how a
|
|
37
|
+
* board earns "it just puts the card back sometimes".
|
|
38
|
+
*/
|
|
39
|
+
const getDistanceToRect = (rect, point) => {
|
|
40
|
+
const dx = Math.max(rect.left - point.x, 0, point.x - (rect.left + rect.width));
|
|
41
|
+
const dy = Math.max(rect.top - point.y, 0, point.y - (rect.top + rect.height));
|
|
42
|
+
return Math.sqrt(dx * dx + dy * dy);
|
|
43
|
+
};
|
|
44
|
+
/**
|
|
45
|
+
* Where in a lane a point lands: the number of cards whose midpoint
|
|
46
|
+
* is above it.
|
|
47
|
+
*
|
|
48
|
+
* Midpoints rather than edges, so the insertion flips when the
|
|
49
|
+
* pointer passes the *centre* of a card. Against edges, the top
|
|
50
|
+
* half of every card is a dead zone that means "same place you
|
|
51
|
+
* already were", and the indicator stutters.
|
|
52
|
+
*/
|
|
53
|
+
export const chooseDropIndex = (cardRects, point) => cardRects.filter((rect) => point.y > rect.top + rect.height / 2).length;
|
|
54
|
+
/**
|
|
55
|
+
* The lane and index a pointer is currently over.
|
|
56
|
+
*
|
|
57
|
+
* Hit test first, nearest-lane second, and `null` only when there
|
|
58
|
+
* are no lanes at all — a drag that started has to end somewhere.
|
|
59
|
+
*/
|
|
60
|
+
export const chooseDropTarget = (lanes, point) => {
|
|
61
|
+
if (lanes.length === 0) {
|
|
62
|
+
return null;
|
|
63
|
+
}
|
|
64
|
+
const hitLane = lanes.find((lane) => getIsPointInside(lane.rect, point));
|
|
65
|
+
const lane = hitLane ??
|
|
66
|
+
lanes.reduce((nearest, candidate) => getDistanceToRect(candidate.rect, point) <
|
|
67
|
+
getDistanceToRect(nearest.rect, point)
|
|
68
|
+
? candidate
|
|
69
|
+
: nearest);
|
|
70
|
+
return {
|
|
71
|
+
index: chooseDropIndex(lane.cardRects, point),
|
|
72
|
+
laneKey: lane.key,
|
|
73
|
+
};
|
|
74
|
+
};
|
|
75
|
+
/**
|
|
76
|
+
* Whether a move would actually change anything.
|
|
77
|
+
*
|
|
78
|
+
* The subtle half is `toIndex`. Taking a card out of its own lane
|
|
79
|
+
* shifts every card below it up by one, so "drop it back where it
|
|
80
|
+
* was" arrives as both `fromIndex` and `fromIndex + 1` depending on
|
|
81
|
+
* which side of its own midpoint the pointer ended on. Both are
|
|
82
|
+
* no-ops, and a board that fires `onMove` for them makes a consumer
|
|
83
|
+
* re-render — and, if the consumer is optimistic, flash — for a
|
|
84
|
+
* drag that went nowhere.
|
|
85
|
+
*/
|
|
86
|
+
export const getIsMoveMeaningful = ({ fromIndex, fromLaneKey, toIndex, toLaneKey, }) => fromLaneKey !== toLaneKey ||
|
|
87
|
+
(toIndex !== fromIndex && toIndex !== fromIndex + 1);
|
|
88
|
+
/**
|
|
89
|
+
* Where a card ends up once it has been removed from its old
|
|
90
|
+
* position.
|
|
91
|
+
*
|
|
92
|
+
* A within-lane move is quoted against the list *including* the
|
|
93
|
+
* card being moved — that is what the pointer was over — but a
|
|
94
|
+
* consumer splicing an array removes it first, so every index below
|
|
95
|
+
* the origin is one too high. Folding that here means the consumer's
|
|
96
|
+
* handler is `splice(from, 1)` then `splice(to, 0, item)` with no
|
|
97
|
+
* arithmetic of its own, which is the version nobody gets wrong.
|
|
98
|
+
*/
|
|
99
|
+
export const toSettledIndex = ({ fromIndex, fromLaneKey, toIndex, toLaneKey, }) => fromLaneKey === toLaneKey && toIndex > fromIndex
|
|
100
|
+
? toIndex - 1
|
|
101
|
+
: toIndex;
|
|
102
|
+
/**
|
|
103
|
+
* What the live region says after a move.
|
|
104
|
+
*
|
|
105
|
+
* A sentence rather than a status word, and it names the
|
|
106
|
+
* **destination position** as well as the lane, because "moved to In
|
|
107
|
+
* Progress" leaves a screen-reader user with no idea whether the
|
|
108
|
+
* card went to the top of the lane or the bottom of thirty. That is
|
|
109
|
+
* the single piece of feedback a sighted user gets for free from
|
|
110
|
+
* watching the card land, and it is the one every drag-and-drop
|
|
111
|
+
* implementation forgets to say out loud.
|
|
112
|
+
*
|
|
113
|
+
* One-based, because it is read to a person.
|
|
114
|
+
*/
|
|
115
|
+
export const describeMove = ({ index, laneLabel, laneSize, title, }) => `Moved ${title} to ${laneLabel}, position ${index + 1} of ${laneSize}.`;
|
|
116
|
+
//# sourceMappingURL=boardMove.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"boardMove.js","sourceRoot":"","sources":["../../src/Board/boardMove.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import type { BoardDropLane, BoardDropTarget } from "./boardMove.ts";
|
|
2
|
+
export type BoardDragOrigin = {
|
|
3
|
+
fromIndex: number;
|
|
4
|
+
fromLaneKey: string;
|
|
5
|
+
itemKey: string;
|
|
6
|
+
};
|
|
7
|
+
export type BoardDragState = BoardDragOrigin & {
|
|
8
|
+
/**
|
|
9
|
+
* Where the indicator is drawn, in pixels from the top of the
|
|
10
|
+
* target lane's list box. `null` while the pointer is over no
|
|
11
|
+
* lane the snapshot knows about.
|
|
12
|
+
*/
|
|
13
|
+
indicatorOffset: number | null;
|
|
14
|
+
target: BoardDropTarget | null;
|
|
15
|
+
};
|
|
16
|
+
type LaneSnapshot = BoardDropLane & {
|
|
17
|
+
gap: number;
|
|
18
|
+
listTop: number;
|
|
19
|
+
};
|
|
20
|
+
/**
|
|
21
|
+
* Where the line goes for a given insertion index — the midpoint of
|
|
22
|
+
* the gap the card would be dropped into, in the list's own
|
|
23
|
+
* coordinates.
|
|
24
|
+
*
|
|
25
|
+
* Split out and exported because it is the one piece of this file
|
|
26
|
+
* that is pure arithmetic over the snapshot, and the three cases
|
|
27
|
+
* (before everything, between two, after everything) are exactly
|
|
28
|
+
* where an off-by-one lives.
|
|
29
|
+
*/
|
|
30
|
+
export declare const getIndicatorOffset: (lane: LaneSnapshot, index: number) => number;
|
|
31
|
+
export declare const useBoardDrag: ({ onDrop, }: {
|
|
32
|
+
onDrop: (origin: BoardDragOrigin, target: BoardDropTarget) => void;
|
|
33
|
+
}) => {
|
|
34
|
+
drag: BoardDragState | null;
|
|
35
|
+
registerLane: (laneKey: string) => (element: HTMLElement | null) => void;
|
|
36
|
+
startDrag: (pointerEvent: {
|
|
37
|
+
clientX: number;
|
|
38
|
+
clientY: number;
|
|
39
|
+
currentTarget: EventTarget & Element;
|
|
40
|
+
pointerId: number;
|
|
41
|
+
}, origin: BoardDragOrigin) => void;
|
|
42
|
+
};
|
|
43
|
+
export {};
|
|
44
|
+
//# sourceMappingURL=useBoardDrag.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useBoardDrag.d.ts","sourceRoot":"","sources":["../../src/Board/useBoardDrag.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EACV,aAAa,EACb,eAAe,EAEhB,MAAM,gBAAgB,CAAA;AAwDvB,MAAM,MAAM,eAAe,GAAG;IAC5B,SAAS,EAAE,MAAM,CAAA;IACjB,WAAW,EAAE,MAAM,CAAA;IACnB,OAAO,EAAE,MAAM,CAAA;CAChB,CAAA;AAED,MAAM,MAAM,cAAc,GAAG,eAAe,GAAG;IAC7C;;;;OAIG;IACH,eAAe,EAAE,MAAM,GAAG,IAAI,CAAA;IAC9B,MAAM,EAAE,eAAe,GAAG,IAAI,CAAA;CAC/B,CAAA;AAED,KAAK,YAAY,GAAG,aAAa,GAAG;IAClC,GAAG,EAAE,MAAM,CAAA;IACX,OAAO,EAAE,MAAM,CAAA;CAChB,CAAA;AAaD;;;;;;;;;GASG;AACH,eAAO,MAAM,kBAAkB,GAC7B,MAAM,YAAY,EAClB,OAAO,MAAM,KACZ,MA2BF,CAAA;AAED,eAAO,MAAM,YAAY,GAAI,aAE1B;IACD,MAAM,EAAE,CACN,MAAM,EAAE,eAAe,EACvB,MAAM,EAAE,eAAe,KACpB,IAAI,CAAA;CACV,KAAG;IACF,IAAI,EAAE,cAAc,GAAG,IAAI,CAAA;IAC3B,YAAY,EAAE,CACZ,OAAO,EAAE,MAAM,KACZ,CAAC,OAAO,EAAE,WAAW,GAAG,IAAI,KAAK,IAAI,CAAA;IAC1C,SAAS,EAAE,CACT,YAAY,EAAE;QACZ,OAAO,EAAE,MAAM,CAAA;QACf,OAAO,EAAE,MAAM,CAAA;QACf,aAAa,EAAE,WAAW,GAAG,OAAO,CAAA;QACpC,SAAS,EAAE,MAAM,CAAA;KAClB,EACD,MAAM,EAAE,eAAe,KACpB,IAAI,CAAA;CA2PV,CAAA"}
|