@stll/ui 0.38.0 → 0.39.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.
@@ -1,527 +0,0 @@
1
- "use client";
2
- import { GripVerticalIcon } from "../icons.js";
3
- import { cn } from "../lib/utils.js";
4
- import "../lib/overlay-layer.js";
5
- import { Button } from "../components/button.js";
6
- import { KANBAN_BOARD_COLLISION_DETECTION, KANBAN_DROP_TARGET_TYPES, clearKanbanKeyboardTarget, getKanbanKeyboardTargetState, isKanbanDropSettled, kanbanKeyboardCoordinates } from "./sortable-interactions.logic.js";
7
- import { isActiveTouchChange } from "./touch-identity.js";
8
- import { jsx, jsxs } from "react/jsx-runtime";
9
- import * as React$1 from "react";
10
- import { createPortal } from "react-dom";
11
- import { AutoScrollActivator, DndContext, DragOverlay, KeyboardSensor, MouseSensor, TouchSensor, useDroppable, useSensor, useSensors } from "@dnd-kit/core";
12
- import { SortableContext, useSortable } from "@dnd-kit/sortable";
13
- //#region src/kanban/sortable-interactions.tsx
14
- const KANBAN_MOUSE_ACTIVATION_DISTANCE = 8;
15
- /** Above sticky board chrome, below app-level floating surfaces. */
16
- const KANBAN_DRAG_OVERLAY_Z_INDEX = 75;
17
- const KANBAN_BOARD_AUTO_SCROLL_OPTIONS = {
18
- acceleration: 10,
19
- activator: AutoScrollActivator.Pointer,
20
- threshold: {
21
- x: .15,
22
- y: .15
23
- }
24
- };
25
- const KANBAN_SORTABLE_ACTIVATION_MODES = {
26
- CARD: "card",
27
- HANDLE: "handle",
28
- ITEM: "item"
29
- };
30
- const KANBAN_TOUCH_ACTIVATION_CONSTRAINT = {
31
- delay: 150,
32
- tolerance: 8
33
- };
34
- const KANBAN_KEYBOARD_CODES = {
35
- cancel: ["Escape"],
36
- end: [
37
- "Space",
38
- "Enter",
39
- "Tab"
40
- ]
41
- };
42
- const KANBAN_KEYBOARD_TARGET_RETRY_LIMIT = 60;
43
- /** A render commit, not a virtual scroll, so the budget stays short. */
44
- const KANBAN_DROP_SETTLE_FRAME_LIMIT = 10;
45
- /**
46
- * Defers a drop until dnd-kit has published the collision target it computed.
47
- * Every sensor ends a drag from a single input event, so without this wait the
48
- * drop resolves against the target published before that event.
49
- */
50
- const endWhenDropTargetSettled = (context, end) => {
51
- let framesWaited = 0;
52
- const attempt = () => {
53
- if (framesWaited >= KANBAN_DROP_SETTLE_FRAME_LIMIT || isKanbanDropSettled(context.current)) {
54
- end();
55
- return;
56
- }
57
- framesWaited += 1;
58
- requestAnimationFrame(attempt);
59
- };
60
- attempt();
61
- };
62
- const hasTouchLists = (event) => "changedTouches" in event && "touches" in event;
63
- const getTouchIdentifier = (event) => {
64
- if (!hasTouchLists(event)) return null;
65
- return event.changedTouches.item(0)?.identifier ?? event.touches.item(0)?.identifier ?? null;
66
- };
67
- const getTouchIdentifiers = (touches) => {
68
- const identifiers = [];
69
- for (let index = 0; index < touches.length; index += 1) {
70
- const identifier = touches.item(index)?.identifier;
71
- if (identifier !== void 0) identifiers.push(identifier);
72
- }
73
- return identifiers;
74
- };
75
- /**
76
- * Keeps a delayed touch drag bound to the finger that activated it.
77
- *
78
- * dnd-kit's stock touch sensor attaches document-level lifecycle listeners,
79
- * so a second finger can otherwise move, end, or cancel the active drag. The
80
- * capture listeners below suppress only secondary touch changes before those
81
- * listeners receive them; browser scrolling remains native because no default
82
- * action is prevented here.
83
- */
84
- var KanbanTouchSensor = class extends TouchSensor {
85
- identityListeners;
86
- ownerDocument;
87
- touchIdentifier;
88
- constructor(props) {
89
- const identityListeners = new AbortController();
90
- super({
91
- ...props,
92
- onAbort: (active) => {
93
- identityListeners.abort();
94
- props.onAbort(active);
95
- },
96
- onCancel: () => {
97
- identityListeners.abort();
98
- props.onCancel();
99
- },
100
- onEnd: () => {
101
- identityListeners.abort();
102
- endWhenDropTargetSettled(props.context, () => {
103
- props.onEnd();
104
- });
105
- }
106
- });
107
- this.identityListeners = identityListeners;
108
- this.ownerDocument = getTouchDocument(props.event);
109
- this.touchIdentifier = getTouchIdentifier(props.event);
110
- this.ownerDocument.addEventListener("touchmove", this.handleTouchMove, {
111
- capture: true,
112
- passive: false,
113
- signal: identityListeners.signal
114
- });
115
- this.ownerDocument.addEventListener("touchend", this.handleTouchEnd, {
116
- capture: true,
117
- signal: identityListeners.signal
118
- });
119
- this.ownerDocument.addEventListener("touchcancel", this.handleTouchCancel, {
120
- capture: true,
121
- signal: identityListeners.signal
122
- });
123
- }
124
- handleTouchMove = (event) => {
125
- if (this.isPrimaryTouchChange(event)) return;
126
- event.stopImmediatePropagation();
127
- };
128
- handleTouchEnd = (event) => {
129
- if (!this.isPrimaryTouchChange(event)) {
130
- event.stopImmediatePropagation();
131
- return;
132
- }
133
- this.detachIdentityListeners();
134
- };
135
- handleTouchCancel = (event) => {
136
- if (!this.isPrimaryTouchChange(event)) {
137
- event.stopImmediatePropagation();
138
- return;
139
- }
140
- this.detachIdentityListeners();
141
- };
142
- isPrimaryTouchChange = (event) => isActiveTouchChange({
143
- activeTouchIdentifier: this.touchIdentifier,
144
- changedTouchIdentifiers: getTouchIdentifiers(event.changedTouches)
145
- });
146
- detachIdentityListeners = () => {
147
- this.identityListeners.abort();
148
- };
149
- };
150
- /** The stock mouse sensor, holding its drop until the board target settles. */
151
- var KanbanMouseSensor = class extends MouseSensor {
152
- constructor(props) {
153
- super({
154
- ...props,
155
- onEnd: () => {
156
- endWhenDropTargetSettled(props.context, () => {
157
- props.onEnd();
158
- });
159
- }
160
- });
161
- }
162
- };
163
- /**
164
- * Applies board coordinates without the stock sensor's one-dimensional source
165
- * scroll clamping. The coordinate getter moves to the typed target and then
166
- * scrolls that virtual item into view.
167
- */
168
- var KanbanKeyboardSensor = class {
169
- static activators = KeyboardSensor.activators;
170
- autoScrollEnabled = false;
171
- /** Input handling, released as soon as a drag stops accepting keys. */
172
- listeners = new AbortController();
173
- /**
174
- * The drag itself, released only once it resolves. Ending must not stop the
175
- * queued retry that still has to bring a pending virtual target into view.
176
- */
177
- lifetime = new AbortController();
178
- props;
179
- moveSequence = 0;
180
- referenceCoordinates = null;
181
- constructor(props) {
182
- this.props = props;
183
- props.onStart({
184
- x: 0,
185
- y: 0
186
- });
187
- const ownerDocument = getTouchDocument(props.event);
188
- ownerDocument.addEventListener("visibilitychange", this.handleCancel, { signal: this.listeners.signal });
189
- ownerDocument.defaultView?.addEventListener("resize", this.handleCancel, { signal: this.listeners.signal });
190
- ownerDocument.addEventListener("keydown", this.handleKeyDown, { signal: this.listeners.signal });
191
- }
192
- handleCancel = () => {
193
- if (this.lifetime.signal.aborted) return;
194
- this.moveSequence += 1;
195
- this.listeners.abort();
196
- this.finish(() => {
197
- this.props.onCancel();
198
- });
199
- };
200
- finish = (resolve) => {
201
- clearKanbanKeyboardTarget(this.props.context.current.active?.data.current);
202
- this.lifetime.abort();
203
- resolve();
204
- };
205
- handleKeyDown = (event) => {
206
- if (event === this.props.event) return;
207
- const keyboardCodes = this.props.options.keyboardCodes;
208
- const cancelCodes = keyboardCodes?.cancel ?? KANBAN_KEYBOARD_CODES.cancel;
209
- const endCodes = keyboardCodes?.end ?? KANBAN_KEYBOARD_CODES.end;
210
- if (cancelCodes.some((code) => code === event.code)) {
211
- event.preventDefault();
212
- this.handleCancel();
213
- return;
214
- }
215
- if (endCodes.some((code) => code === event.code)) {
216
- event.preventDefault();
217
- this.listeners.abort();
218
- this.endWhenNavigationResolves(0);
219
- return;
220
- }
221
- this.moveSequence += 1;
222
- this.move(event, this.moveSequence, 0);
223
- };
224
- /**
225
- * A pending target has asked a virtual cell to mount an offscreen row and has
226
- * published nothing, so the queued move retry, not this keypress, decides
227
- * where the item lands. Cancel when that retry gives up: the board never
228
- * reached the row the user navigated to, and the target still published is
229
- * the one they navigated away from.
230
- */
231
- endWhenNavigationResolves = (framesWaited) => {
232
- if (this.lifetime.signal.aborted) return;
233
- const target = getKanbanKeyboardTargetState(this.props.context.current.active?.data.current);
234
- if (target?.type === "pending") {
235
- if (framesWaited >= KANBAN_KEYBOARD_TARGET_RETRY_LIMIT) {
236
- this.finish(() => {
237
- this.props.onCancel();
238
- });
239
- return;
240
- }
241
- requestAnimationFrame(() => {
242
- this.endWhenNavigationResolves(framesWaited + 1);
243
- });
244
- return;
245
- }
246
- if (framesWaited > 0 && target?.type !== "ready") {
247
- this.finish(() => {
248
- this.props.onCancel();
249
- });
250
- return;
251
- }
252
- endWhenDropTargetSettled(this.props.context, () => {
253
- const settled = isKanbanDropSettled(this.props.context.current);
254
- this.finish(() => {
255
- if (settled) {
256
- this.props.onEnd();
257
- return;
258
- }
259
- this.props.onCancel();
260
- });
261
- });
262
- };
263
- move = (event, sequence, retryCount) => {
264
- const currentContext = this.props.context.current;
265
- if (currentContext.active?.id !== this.props.active) {
266
- requestAnimationFrame(() => {
267
- if (this.listeners.signal.aborted || this.moveSequence !== sequence) return;
268
- this.move(event, sequence, retryCount);
269
- });
270
- return;
271
- }
272
- const collisionRect = currentContext.collisionRect;
273
- const currentCoordinates = collisionRect ? {
274
- x: collisionRect.left,
275
- y: collisionRect.top
276
- } : {
277
- x: 0,
278
- y: 0
279
- };
280
- this.referenceCoordinates ??= currentCoordinates;
281
- const activeData = currentContext.active.data.current;
282
- const coordinates = this.props.options.coordinateGetter?.(event, {
283
- active: this.props.active,
284
- context: currentContext,
285
- currentCoordinates
286
- });
287
- if (coordinates === void 0) {
288
- if (getKanbanKeyboardTargetState(activeData)?.type !== "pending") return;
289
- if (retryCount >= KANBAN_KEYBOARD_TARGET_RETRY_LIMIT) {
290
- clearKanbanKeyboardTarget(activeData);
291
- return;
292
- }
293
- requestAnimationFrame(() => {
294
- if (this.lifetime.signal.aborted || this.moveSequence !== sequence) return;
295
- this.move(event, sequence, retryCount + 1);
296
- });
297
- return;
298
- }
299
- this.props.onMove({
300
- x: coordinates.x - this.referenceCoordinates.x,
301
- y: coordinates.y - this.referenceCoordinates.y
302
- });
303
- };
304
- };
305
- const getTouchDocument = (event) => {
306
- if (event.target instanceof Node && event.target.ownerDocument) return event.target.ownerDocument;
307
- return document;
308
- };
309
- const KanbanInteractionReadinessContext = React$1.createContext(true);
310
- /**
311
- * Register a board-level drop target such as an empty cell or terminal lane.
312
- *
313
- * Board consumers should not need to couple their presentation code to the
314
- * underlying drag-and-drop library just to receive an item from a sortable
315
- * context.
316
- */
317
- const useKanbanDropTarget = ({ disabled, id, itemIds, navigation, position }) => {
318
- const interactionReady = React$1.useContext(KanbanInteractionReadinessContext);
319
- const dropTarget = useDroppable({
320
- data: {
321
- itemIds,
322
- navigation,
323
- position,
324
- type: KANBAN_DROP_TARGET_TYPES.CELL
325
- },
326
- disabled: !interactionReady || disabled === true,
327
- id
328
- });
329
- return {
330
- isOver: dropTarget.isOver,
331
- setNodeRef: dropTarget.setNodeRef
332
- };
333
- };
334
- /**
335
- * Input-complete drag context for sortable boards.
336
- *
337
- * The consumer owns identifiers and the result of a drop; this component owns
338
- * the sensor activation rules, keyboard navigation, auto-scroll configuration,
339
- * and overlay lifecycle shared by sortable board UIs.
340
- */
341
- const KanbanSortableBoard = ({ children, onDragEnd, mouseActivationDistance, collisionDetection, keyboardCoordinates, autoScroll, sensors, accessibility, onDragStart, onDragOver, onDragCancel, onInteractionReady, overlay, overlayProps }) => {
342
- const [activeId, setActiveId] = React$1.useState(null);
343
- const [interactionReady, setInteractionReady] = React$1.useState(false);
344
- const interactionReadyNotified = React$1.useRef(false);
345
- const defaultSensors = useKanbanSortableSensors(keyboardCoordinates, mouseActivationDistance);
346
- React$1.useEffect(() => {
347
- const frame = requestAnimationFrame(() => {
348
- setInteractionReady(true);
349
- });
350
- return () => {
351
- cancelAnimationFrame(frame);
352
- };
353
- }, []);
354
- React$1.useEffect(() => {
355
- if (!interactionReady || interactionReadyNotified.current) return;
356
- interactionReadyNotified.current = true;
357
- onInteractionReady?.();
358
- }, [interactionReady, onInteractionReady]);
359
- const handleDragStart = (event) => {
360
- setActiveId(event.active.id);
361
- onDragStart?.(event);
362
- };
363
- const handleDragEnd = (event) => {
364
- setActiveId(null);
365
- onDragEnd(event);
366
- };
367
- const handleDragCancel = (event) => {
368
- setActiveId(null);
369
- onDragCancel?.(event);
370
- };
371
- return /* @__PURE__ */ jsx(KanbanInteractionReadinessContext, {
372
- value: interactionReady,
373
- children: /* @__PURE__ */ jsxs(DndContext, {
374
- autoScroll: autoScroll ?? KANBAN_BOARD_AUTO_SCROLL_OPTIONS,
375
- ...accessibility === void 0 ? {} : { accessibility },
376
- collisionDetection: collisionDetection ?? KANBAN_BOARD_COLLISION_DETECTION,
377
- onDragCancel: handleDragCancel,
378
- onDragEnd: handleDragEnd,
379
- ...onDragOver === void 0 ? {} : { onDragOver },
380
- onDragStart: handleDragStart,
381
- sensors: sensors ?? defaultSensors,
382
- children: [children, overlay && typeof document !== "undefined" ? createPortal(/* @__PURE__ */ jsx(DragOverlay, {
383
- ...overlayProps,
384
- zIndex: overlayProps?.zIndex ?? KANBAN_DRAG_OVERLAY_Z_INDEX,
385
- children: overlay(activeId)
386
- }), document.body) : null]
387
- })
388
- });
389
- };
390
- const useKanbanSortableSensors = (keyboardCoordinates = kanbanKeyboardCoordinates, mouseActivationDistance = 8) => useSensors(useSensor(KanbanMouseSensor, { activationConstraint: { distance: mouseActivationDistance } }), useSensor(KanbanTouchSensor, { activationConstraint: KANBAN_TOUCH_ACTIVATION_CONSTRAINT }), useSensor(KanbanKeyboardSensor, { coordinateGetter: keyboardCoordinates }));
391
- /** A vertical card list that preserves native vertical touch scrolling. */
392
- const KanbanSortableList = ({ className, children, id, items, strategy, disabled, ...props }) => /* @__PURE__ */ jsx(SortableContext, {
393
- ...disabled === void 0 ? {} : { disabled },
394
- ...id === void 0 ? {} : { id },
395
- items,
396
- ...strategy === void 0 ? {} : { strategy },
397
- children: /* @__PURE__ */ jsx("div", {
398
- className: cn("min-h-0 touch-auto overflow-y-auto overscroll-y-contain", className),
399
- ...props,
400
- children
401
- })
402
- });
403
- /** A horizontal column list that keeps the board pan gesture available. */
404
- const KanbanSortableColumns = ({ className, children, id, items, strategy, disabled, ...props }) => /* @__PURE__ */ jsx(SortableContext, {
405
- ...disabled === void 0 ? {} : { disabled },
406
- ...id === void 0 ? {} : { id },
407
- items,
408
- ...strategy === void 0 ? {} : { strategy },
409
- children: /* @__PURE__ */ jsx("div", {
410
- className: cn("flex min-h-0 touch-auto overflow-x-auto overscroll-x-contain", className),
411
- ...props,
412
- children
413
- })
414
- });
415
- /**
416
- * Connect a sortable item and its separate drag handle without making the
417
- * item's content a touch-none activation surface.
418
- */
419
- const useKanbanSortable = ({ activation, id, disabled }) => {
420
- const interactionReady = React$1.useContext(KanbanInteractionReadinessContext);
421
- const data = React$1.useMemo(() => ({
422
- navigation: { current: { type: "idle" } },
423
- type: KANBAN_DROP_TARGET_TYPES.ITEM
424
- }), []);
425
- const sortable = useSortable({
426
- data,
427
- disabled: !interactionReady || disabled === true,
428
- id
429
- });
430
- const setNodeRef = (element) => {
431
- sortable.setNodeRef(element);
432
- if (activation.type === KANBAN_SORTABLE_ACTIVATION_MODES.ITEM) sortable.setActivatorNodeRef(element);
433
- };
434
- const bindings = {
435
- attributes: sortable.attributes,
436
- listeners: sortable.listeners,
437
- setActivatorNodeRef: sortable.setActivatorNodeRef
438
- };
439
- let activator;
440
- if (activation.type === KANBAN_SORTABLE_ACTIVATION_MODES.HANDLE) activator = {
441
- bindings,
442
- type: KANBAN_SORTABLE_ACTIVATION_MODES.HANDLE
443
- };
444
- else if (activation.type === KANBAN_SORTABLE_ACTIVATION_MODES.CARD) activator = {
445
- bindings,
446
- type: KANBAN_SORTABLE_ACTIVATION_MODES.CARD
447
- };
448
- else activator = {
449
- attributes: sortable.attributes,
450
- listeners: sortable.listeners,
451
- type: KANBAN_SORTABLE_ACTIVATION_MODES.ITEM
452
- };
453
- return {
454
- activator,
455
- isDragging: sortable.isDragging,
456
- setNodeRef,
457
- style: {
458
- touchAction: activation.type === KANBAN_SORTABLE_ACTIVATION_MODES.ITEM ? "none" : void 0,
459
- transform: sortable.transform ? `translate3d(${sortable.transform.x}px, ${sortable.transform.y}px, 0)` : void 0,
460
- transition: sortable.transition
461
- }
462
- };
463
- };
464
- /**
465
- * A 44px keyboard-accessible drag activator. The handle is the only board
466
- * surface that disables touch panning, leaving cards and scroll regions free
467
- * for ordinary scrolling.
468
- */
469
- const KanbanDragHandle = ({ bindings, label, className, ...props }) => /* @__PURE__ */ jsx(Button, {
470
- ...props,
471
- ...bindings.attributes,
472
- ...bindings.listeners,
473
- "aria-label": label,
474
- className: cn("size-11 min-h-11 min-w-11 touch-none sm:size-11", className),
475
- ref: (element) => bindings.setActivatorNodeRef(element),
476
- size: "icon-xl",
477
- tooltip: false,
478
- type: "button",
479
- variant: "ghost",
480
- children: /* @__PURE__ */ jsx(GripVerticalIcon, { "aria-hidden": "true" })
481
- });
482
- const KANBAN_CARD_DRAG_SURFACE_INTERACTIVE_SELECTOR = "a,button,input,select,textarea,[contenteditable=true],[role=button],[data-kanban-drag-exempt]";
483
- const isKanbanCardDragSurfaceInteractiveTarget = (target, activator) => {
484
- if (!(target instanceof Element)) return false;
485
- const interactiveTarget = target.closest(KANBAN_CARD_DRAG_SURFACE_INTERACTIVE_SELECTOR);
486
- return interactiveTarget !== null && interactiveTarget !== activator;
487
- };
488
- const getKanbanCardDragSurfaceAttributes = ({ "aria-describedby": _describedBy, "aria-disabled": _disabled, "aria-pressed": _pressed, "aria-roledescription": _roleDescription, role: _role, tabIndex, ...attributes }) => ({
489
- ...attributes,
490
- tabIndex
491
- });
492
- const isKanbanCardDragSurfaceListener = (listener) => typeof listener === "function";
493
- const getKanbanCardDragSurfaceListeners = (listeners) => {
494
- const { onKeyDown, onMouseDown, onPointerDown, onTouchStart, ...rest } = listeners;
495
- const shouldActivate = (event) => !isKanbanCardDragSurfaceInteractiveTarget(event.target, event.currentTarget);
496
- return {
497
- ...rest,
498
- ...!isKanbanCardDragSurfaceListener(onKeyDown) ? {} : { onKeyDown: (event) => {
499
- if (shouldActivate(event)) onKeyDown(event);
500
- } },
501
- ...!isKanbanCardDragSurfaceListener(onMouseDown) ? {} : { onMouseDown: (event) => {
502
- if (shouldActivate(event)) onMouseDown(event);
503
- } },
504
- ...!isKanbanCardDragSurfaceListener(onPointerDown) ? {} : { onPointerDown: (event) => {
505
- if (shouldActivate(event)) onPointerDown(event);
506
- } },
507
- ...!isKanbanCardDragSurfaceListener(onTouchStart) ? {} : { onTouchStart: (event) => {
508
- if (shouldActivate(event)) onTouchStart(event);
509
- } }
510
- };
511
- };
512
- /** A whole-card drag activator that preserves descendant controls and scrolling. */
513
- const KanbanCardDragSurface = ({ bindings, className, ...props }) => {
514
- const { listeners, setActivatorNodeRef: setCardActivatorNodeRef } = bindings;
515
- const setActivatorNodeRef = React$1.useCallback((element) => {
516
- setCardActivatorNodeRef(element);
517
- }, [setCardActivatorNodeRef]);
518
- return /* @__PURE__ */ jsx("div", {
519
- ...props,
520
- ...getKanbanCardDragSurfaceAttributes(bindings.attributes),
521
- ...listeners === void 0 ? {} : getKanbanCardDragSurfaceListeners(listeners),
522
- className: cn("touch-auto", className),
523
- ref: setActivatorNodeRef
524
- });
525
- };
526
- //#endregion
527
- export { KANBAN_BOARD_AUTO_SCROLL_OPTIONS, KANBAN_BOARD_COLLISION_DETECTION, KANBAN_DRAG_OVERLAY_Z_INDEX, KANBAN_MOUSE_ACTIVATION_DISTANCE, KANBAN_SORTABLE_ACTIVATION_MODES, KANBAN_TOUCH_ACTIVATION_CONSTRAINT, KanbanCardDragSurface, KanbanDragHandle, KanbanSortableBoard, KanbanSortableColumns, KanbanSortableList, kanbanKeyboardCoordinates, useKanbanDropTarget, useKanbanSortable, useKanbanSortableSensors };
@@ -1,78 +0,0 @@
1
- import { CollisionDetection, KeyboardCoordinateGetter, SensorContext, UniqueIdentifier } from "@dnd-kit/core";
2
- //#region src/kanban/sortable-interactions.logic.d.ts
3
- export declare const KANBAN_DROP_TARGET_TYPES: {
4
- readonly CELL: "kanban-cell";
5
- readonly ITEM: "kanban-item";
6
- };
7
- export type KanbanSortableCellPosition = {
8
- column: number;
9
- lane: number;
10
- };
11
- export type KanbanVirtualScrollRequest = {
12
- itemId: UniqueIdentifier;
13
- type: "item";
14
- };
15
- export type KanbanCellVirtualNavigation = {
16
- type: "static";
17
- } | {
18
- requestScroll: (request: KanbanVirtualScrollRequest) => void;
19
- type: "virtual";
20
- };
21
- export type KanbanCellDropData = {
22
- itemIds: readonly UniqueIdentifier[];
23
- navigation: KanbanCellVirtualNavigation;
24
- position: KanbanSortableCellPosition;
25
- type: "kanban-cell";
26
- };
27
- type KanbanKeyboardTargetState = {
28
- type: "idle";
29
- } | {
30
- targetId: UniqueIdentifier;
31
- type: "pending";
32
- } | {
33
- targetId: UniqueIdentifier;
34
- type: "ready";
35
- };
36
- export type KanbanItemDropData = {
37
- navigation: {
38
- current: KanbanKeyboardTargetState;
39
- };
40
- type: "kanban-item";
41
- };
42
- type NavigationCell = KanbanCellDropData & {
43
- id: UniqueIdentifier;
44
- };
45
- export declare const isKanbanCellDropData: (value: unknown) => value is KanbanCellDropData;
46
- export declare const getKanbanKeyboardTargetState: (value: unknown) => KanbanKeyboardTargetState | undefined;
47
- /**
48
- * dnd-kit computes collisions while rendering but publishes the resulting drop
49
- * target to its sensor context a render later, and resolves a drop from that
50
- * published value. A drag that ends before the two agree, which every input
51
- * can do because a single move produces a single render, drops the item on the
52
- * previously published target.
53
- *
54
- * Keyboard navigation names its target outright, so it is compared directly: a
55
- * pending target is still waiting for a virtual cell to mount an offscreen row
56
- * and has produced no collision at all, and a ready one is only settled once it
57
- * is the published target. Until then both the collisions and the published
58
- * target still describe where the board was before the user navigated away.
59
- */
60
- export declare const isKanbanDropSettled: ({ active, collisions, over }: Pick<SensorContext, "active" | "collisions" | "over">) => boolean;
61
- export declare const clearKanbanKeyboardTarget: (value: unknown) => void;
62
- /**
63
- * Pointer and touch input must be inside a registered board target. Once that
64
- * boundary is established, closest corners gives stable ranking across cells.
65
- */
66
- export declare const KANBAN_BOARD_COLLISION_DETECTION: CollisionDetection;
67
- type NavigationDirection = "down" | "left" | "right" | "up";
68
- type GetKeyboardTargetOptions = {
69
- activeId: UniqueIdentifier;
70
- cells: readonly NavigationCell[];
71
- currentCellId: UniqueIdentifier;
72
- currentOverId: UniqueIdentifier;
73
- direction: NavigationDirection;
74
- };
75
- export declare const getKanbanKeyboardTarget: ({ activeId, cells, currentCellId, currentOverId, direction }: GetKeyboardTargetOptions) => UniqueIdentifier | undefined;
76
- /** Ordered two-dimensional navigation over item and empty-cell targets. */
77
- export declare const kanbanKeyboardCoordinates: KeyboardCoordinateGetter;
78
- //#endregion