@elliemae/ds-drag-and-drop 3.70.0-next.49 → 3.70.0-next.50

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.
@@ -92,6 +92,7 @@ const useMultiTreeDndkitConfig = ({
92
92
  );
93
93
  const containersRef = (0, import_react.useRef)([]);
94
94
  const containerNodesRef = (0, import_react.useRef)({});
95
+ const latestDropIndicatorPositionRef = (0, import_react.useRef)(dropIndicatorPosition);
95
96
  const visibleFlattenedItems = (0, import_react.useMemo)(() => {
96
97
  const dictionary = {};
97
98
  const collapsedItems = {};
@@ -181,12 +182,24 @@ const useMultiTreeDndkitConfig = ({
181
182
  });
182
183
  const announcements = (0, import_useTreeAnnouncements.useTreeAnnouncements)({
183
184
  flattenedItems,
185
+ visibleFlattenedItems,
184
186
  flattenedItemsDictionary,
185
187
  containersRef,
186
188
  dropIndicatorPosition,
189
+ latestDropIndicatorPositionRef,
190
+ isExpandable,
187
191
  containerNodesRef
188
192
  });
189
- const collisionDetection = (0, import_customCollisionDetection.customCollisionDetection)(sensorContext);
193
+ const rawCollisionDetection = (0, import_react.useMemo)(() => (0, import_customCollisionDetection.customCollisionDetection)(sensorContext), []);
194
+ const collisionDetection = (0, import_react.useCallback)(
195
+ (collisionArgs) => {
196
+ const collisions = rawCollisionDetection(collisionArgs);
197
+ const fresh = collisions?.[0]?.data?.dropIndicatorPosition;
198
+ if (fresh !== void 0) latestDropIndicatorPositionRef.current = fresh;
199
+ return collisions;
200
+ },
201
+ [rawCollisionDetection]
202
+ );
190
203
  const dndContextProps = (0, import_react.useMemo)(
191
204
  () => ({
192
205
  accessibility: {
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/tree/useMultiTreeDndkitConfig.tsx", "../../../../../../scripts/build/transpile/react-shim.js"],
4
- "sourcesContent": ["/* eslint-disable max-statements */\n/* eslint-disable max-lines */\nimport { useState, useEffect, useMemo, useRef } from 'react';\nimport { type TypescriptHelpersT } from '@elliemae/ds-typescript-helpers';\nimport type { Active, MeasuringConfiguration, Modifier, Over, UniqueIdentifier } from '@dnd-kit/core';\nimport { useSensor, useSensors, KeyboardSensor, PointerSensor, MeasuringStrategy } from '@dnd-kit/core';\nimport { useTreePreviewHandlers } from './useTreePreviewHandlers.js';\nimport { getTreeKeyboardCoordinates } from './getTreeKeyboardCoordinates.js';\nimport { getProjection, removeChildrenOf } from './utilities.js';\nimport { useTreeActionHandlers } from './useTreeActionHandlers.js';\nimport type { DnDKitTree } from './types.js';\nimport { DropIndicatorPosition } from './constants.js';\nimport { customCollisionDetection } from './customCollisionDetection.js';\nimport { useTreeAnnouncements } from './useTreeAnnouncements.js';\n\n// we make space for the drop indicator\n// if second parameter is true, the space will be done on the horizontal axis\nexport const adjustTranslate = (isHorizontalDnD: boolean, sortedIds: Record<string, UniqueIdentifier[]>): Modifier => {\n const func: Modifier = ({ transform, activatorEvent, active }) => {\n const newTransform = {\n ...transform,\n };\n if (isHorizontalDnD) {\n newTransform.x = transform.x + 25;\n } else {\n newTransform.x = transform.x + 15;\n if (['Enter', 'Space'].includes((activatorEvent as KeyboardEvent)?.code)) {\n const activeContainerId =\n Object.keys(sortedIds).find((key) => sortedIds[key].includes(active?.id ?? '')) ?? Object.keys(sortedIds)[0];\n const isLast = active?.id === sortedIds[activeContainerId][sortedIds[activeContainerId].length - 1];\n const keyboardTranslate = ((isLast ? -1 : 1) * (active?.rect?.current?.initial?.height ?? 0)) / 2;\n newTransform.y = transform.y + keyboardTranslate;\n }\n }\n return newTransform;\n };\n return func;\n};\n\nconst measuring: Partial<MeasuringConfiguration> = {\n droppable: {\n strategy: MeasuringStrategy.Always,\n },\n};\n\nexport const useMultiTreeDndkitConfig = <T,>({\n flattenedItems,\n onReorder,\n isHorizontalDnD = false,\n isExpandable = false,\n getIsDropValid = () => true,\n maxDragAndDropLevel = Infinity,\n}: DnDKitTree.UseMultiTreeConfigArgs<T>): DnDKitTree.UseTreeConfigReturn<T> => {\n const [active, setActive] = useState<Active | null>(null);\n const [over, setOver] = useState<Over | null>(null);\n const [dropIndicatorPosition, setDropIndicatorPosition] = useState<DnDKitTree.DropIndicatorPositionValues>(\n DropIndicatorPosition.After,\n );\n // Sorted ids for the library\n const sortedIds = useMemo(\n () =>\n Object.keys(flattenedItems).reduce(\n (acc, curr) => {\n acc[curr] = flattenedItems[curr].map((item) => item.uid);\n return acc;\n },\n {} as Record<string, UniqueIdentifier[]>,\n ),\n [flattenedItems],\n );\n\n /**\n * List of unique containers\n */\n const containersRef = useRef([] as string[]);\n\n /**\n * Container id -> DOM node, registered by DSSortableContainer. Used to resolve the destination\n * container's accessible name for screen-reader announcements on cross-container moves.\n */\n const containerNodesRef = useRef({} as Record<string, HTMLElement | null>);\n\n const visibleFlattenedItems = useMemo(() => {\n const dictionary: Record<string, DnDKitTree.Item<T>[]> = {};\n const collapsedItems: Record<UniqueIdentifier, boolean | undefined> = {};\n Object.keys(flattenedItems).forEach((container) => {\n const itemWithNoActiveChildren = removeChildrenOf(flattenedItems[container], active?.id);\n\n dictionary[container] = [];\n\n itemWithNoActiveChildren.forEach((item) => {\n if (item.parentId === null || !collapsedItems[item.parentId]) {\n dictionary[container].push(item);\n }\n collapsedItems[item.uid] = item.collapsed;\n });\n });\n return dictionary;\n }, [active?.id, flattenedItems]);\n\n /**\n * Dictionary from UID to ITEM\n * This dictionary is computed since on every DnD move, I need to know the\n * depth of a particular item, so O(1) per DnD move instead of O(#ITEMS)\n */\n const flattenedItemsDictionary = useMemo(() => {\n const dictionary: Record<UniqueIdentifier, TypescriptHelpersT.ObjectValues<typeof flattenedItems>[0]> = {};\n Object.keys(flattenedItems).forEach((container) => {\n flattenedItems[container].forEach((item) => {\n dictionary[item.uid] = item;\n if (!item.container) return;\n if (!containersRef.current.includes(item.container)) containersRef.current.push(item.container);\n });\n });\n return dictionary;\n }, [flattenedItems]);\n\n const isDropValid = useMemo(() => {\n if (!active || !over) return true;\n return getIsDropValid(\n flattenedItemsDictionary[active.id],\n flattenedItemsDictionary[over.id],\n dropIndicatorPosition,\n );\n }, [getIsDropValid, flattenedItemsDictionary, active, over, dropIndicatorPosition]);\n\n const modifiers: Modifier[] = useMemo(\n () => [adjustTranslate(isHorizontalDnD, sortedIds)],\n [isHorizontalDnD, sortedIds],\n );\n\n const sensorContext: DnDKitTree.SensorContext<T> = useRef({\n items: flattenedItems,\n dropIndicatorPosition,\n setDropIndicatorPosition,\n isHorizontalDnD,\n maxDragAndDropLevel,\n active,\n flattenedItemsDictionary,\n containersRef,\n });\n\n useEffect(() => {\n sensorContext.current = {\n items: flattenedItems,\n dropIndicatorPosition,\n isHorizontalDnD,\n maxDragAndDropLevel,\n active,\n flattenedItemsDictionary,\n containersRef,\n };\n }, [flattenedItems, dropIndicatorPosition, isHorizontalDnD, maxDragAndDropLevel, active, flattenedItemsDictionary]);\n\n const coordinateGetter = useMemo(() => getTreeKeyboardCoordinates(sensorContext), []);\n\n const sensors = useSensors(useSensor(PointerSensor), useSensor(KeyboardSensor, { coordinateGetter }));\n\n // where is the activeItem being positioned (depth and parent)\n const projected = useMemo(\n () =>\n over && active\n ? getProjection(\n visibleFlattenedItems,\n flattenedItemsDictionary,\n over,\n dropIndicatorPosition,\n isExpandable,\n active,\n )\n : null,\n [over, visibleFlattenedItems, flattenedItemsDictionary, dropIndicatorPosition, isExpandable, active],\n );\n\n const dragPreviewHandlers = useTreePreviewHandlers({\n setActive,\n setOver,\n setDropIndicatorPosition,\n });\n\n const dragActionHandlers = useTreeActionHandlers({\n ...dragPreviewHandlers,\n onReorder,\n projected,\n flattenedItems,\n dropIndicatorPosition,\n isDropValid,\n flattenedItemsDictionary,\n containersRef,\n });\n\n const announcements = useTreeAnnouncements({\n flattenedItems,\n flattenedItemsDictionary,\n containersRef,\n dropIndicatorPosition,\n containerNodesRef,\n });\n\n const collisionDetection = customCollisionDetection(sensorContext);\n\n const dndContextProps = useMemo(\n () => ({\n accessibility: {\n announcements,\n container: document.body,\n },\n modifiers,\n sensors,\n measuring,\n collisionDetection,\n ...dragActionHandlers,\n }),\n [announcements, modifiers, sensors, collisionDetection, dragActionHandlers],\n );\n\n const sortableContextProps = useMemo(\n () =>\n Object.keys(sortedIds).reduce(\n (acc, curr) => {\n acc[curr] = {\n items: sortedIds[curr],\n strategy: () => null,\n };\n return acc;\n },\n {} as Record<string, DnDKitTree.SortableContextPropsType>,\n ),\n [sortedIds],\n );\n\n const activeItem = useMemo(() => {\n if (!active) return null;\n return flattenedItemsDictionary[active.id];\n }, [active, flattenedItemsDictionary]);\n\n const overItem = useMemo(() => {\n if (!over) return null;\n return flattenedItemsDictionary[over.id];\n }, [flattenedItemsDictionary, over]);\n\n return {\n dndContextProps,\n sortableContextProps,\n isDropValid,\n active,\n activeItem,\n over,\n overItem,\n dropIndicatorPosition,\n containerNodesRef,\n };\n};\n", "import * as React from 'react';\nexport { React };\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["/* eslint-disable max-statements */\n/* eslint-disable max-lines */\nimport { useState, useEffect, useMemo, useRef, useCallback } from 'react';\nimport { type TypescriptHelpersT } from '@elliemae/ds-typescript-helpers';\nimport type {\n Active,\n CollisionDetection,\n MeasuringConfiguration,\n Modifier,\n Over,\n UniqueIdentifier,\n} from '@dnd-kit/core';\nimport { useSensor, useSensors, KeyboardSensor, PointerSensor, MeasuringStrategy } from '@dnd-kit/core';\nimport { useTreePreviewHandlers } from './useTreePreviewHandlers.js';\nimport { getTreeKeyboardCoordinates } from './getTreeKeyboardCoordinates.js';\nimport { getProjection, removeChildrenOf } from './utilities.js';\nimport { useTreeActionHandlers } from './useTreeActionHandlers.js';\nimport type { DnDKitTree } from './types.js';\nimport { DropIndicatorPosition } from './constants.js';\nimport { customCollisionDetection } from './customCollisionDetection.js';\nimport { useTreeAnnouncements } from './useTreeAnnouncements.js';\n\n// we make space for the drop indicator\n// if second parameter is true, the space will be done on the horizontal axis\nexport const adjustTranslate = (isHorizontalDnD: boolean, sortedIds: Record<string, UniqueIdentifier[]>): Modifier => {\n const func: Modifier = ({ transform, activatorEvent, active }) => {\n const newTransform = {\n ...transform,\n };\n if (isHorizontalDnD) {\n newTransform.x = transform.x + 25;\n } else {\n newTransform.x = transform.x + 15;\n if (['Enter', 'Space'].includes((activatorEvent as KeyboardEvent)?.code)) {\n const activeContainerId =\n Object.keys(sortedIds).find((key) => sortedIds[key].includes(active?.id ?? '')) ?? Object.keys(sortedIds)[0];\n const isLast = active?.id === sortedIds[activeContainerId][sortedIds[activeContainerId].length - 1];\n const keyboardTranslate = ((isLast ? -1 : 1) * (active?.rect?.current?.initial?.height ?? 0)) / 2;\n newTransform.y = transform.y + keyboardTranslate;\n }\n }\n return newTransform;\n };\n return func;\n};\n\nconst measuring: Partial<MeasuringConfiguration> = {\n droppable: {\n strategy: MeasuringStrategy.Always,\n },\n};\n\nexport const useMultiTreeDndkitConfig = <T,>({\n flattenedItems,\n onReorder,\n isHorizontalDnD = false,\n isExpandable = false,\n getIsDropValid = () => true,\n maxDragAndDropLevel = Infinity,\n}: DnDKitTree.UseMultiTreeConfigArgs<T>): DnDKitTree.UseTreeConfigReturn<T> => {\n const [active, setActive] = useState<Active | null>(null);\n const [over, setOver] = useState<Over | null>(null);\n const [dropIndicatorPosition, setDropIndicatorPosition] = useState<DnDKitTree.DropIndicatorPositionValues>(\n DropIndicatorPosition.After,\n );\n // Sorted ids for the library\n const sortedIds = useMemo(\n () =>\n Object.keys(flattenedItems).reduce(\n (acc, curr) => {\n acc[curr] = flattenedItems[curr].map((item) => item.uid);\n return acc;\n },\n {} as Record<string, UniqueIdentifier[]>,\n ),\n [flattenedItems],\n );\n\n /**\n * List of unique containers\n */\n const containersRef = useRef([] as string[]);\n\n /**\n * Container id -> DOM node, registered by DSSortableContainer. Used to resolve the destination\n * container's accessible name for screen-reader announcements on cross-container moves.\n */\n const containerNodesRef = useRef({} as Record<string, HTMLElement | null>);\n\n /**\n * The drop-indicator position from the *latest* collision computation. `dropIndicatorPosition` state\n * lags by one render, so announcements \u2014 which fire during the drag event, before that state commits \u2014\n * would read a stale position whenever the indicator side flips (e.g. crossing above the first item of\n * a container). Collision detection runs before the announcement for the same event, so it writes the\n * fresh value here for the announcement to read, keeping the spoken slot in lockstep with the indicator.\n */\n const latestDropIndicatorPositionRef = useRef<DnDKitTree.DropIndicatorPositionValues>(dropIndicatorPosition);\n\n const visibleFlattenedItems = useMemo(() => {\n const dictionary: Record<string, DnDKitTree.Item<T>[]> = {};\n const collapsedItems: Record<UniqueIdentifier, boolean | undefined> = {};\n Object.keys(flattenedItems).forEach((container) => {\n const itemWithNoActiveChildren = removeChildrenOf(flattenedItems[container], active?.id);\n\n dictionary[container] = [];\n\n itemWithNoActiveChildren.forEach((item) => {\n if (item.parentId === null || !collapsedItems[item.parentId]) {\n dictionary[container].push(item);\n }\n collapsedItems[item.uid] = item.collapsed;\n });\n });\n return dictionary;\n }, [active?.id, flattenedItems]);\n\n /**\n * Dictionary from UID to ITEM\n * This dictionary is computed since on every DnD move, I need to know the\n * depth of a particular item, so O(1) per DnD move instead of O(#ITEMS)\n */\n const flattenedItemsDictionary = useMemo(() => {\n const dictionary: Record<UniqueIdentifier, TypescriptHelpersT.ObjectValues<typeof flattenedItems>[0]> = {};\n Object.keys(flattenedItems).forEach((container) => {\n flattenedItems[container].forEach((item) => {\n dictionary[item.uid] = item;\n if (!item.container) return;\n if (!containersRef.current.includes(item.container)) containersRef.current.push(item.container);\n });\n });\n return dictionary;\n }, [flattenedItems]);\n\n const isDropValid = useMemo(() => {\n if (!active || !over) return true;\n return getIsDropValid(\n flattenedItemsDictionary[active.id],\n flattenedItemsDictionary[over.id],\n dropIndicatorPosition,\n );\n }, [getIsDropValid, flattenedItemsDictionary, active, over, dropIndicatorPosition]);\n\n const modifiers: Modifier[] = useMemo(\n () => [adjustTranslate(isHorizontalDnD, sortedIds)],\n [isHorizontalDnD, sortedIds],\n );\n\n const sensorContext: DnDKitTree.SensorContext<T> = useRef({\n items: flattenedItems,\n dropIndicatorPosition,\n setDropIndicatorPosition,\n isHorizontalDnD,\n maxDragAndDropLevel,\n active,\n flattenedItemsDictionary,\n containersRef,\n });\n\n useEffect(() => {\n sensorContext.current = {\n items: flattenedItems,\n dropIndicatorPosition,\n isHorizontalDnD,\n maxDragAndDropLevel,\n active,\n flattenedItemsDictionary,\n containersRef,\n };\n }, [flattenedItems, dropIndicatorPosition, isHorizontalDnD, maxDragAndDropLevel, active, flattenedItemsDictionary]);\n\n const coordinateGetter = useMemo(() => getTreeKeyboardCoordinates(sensorContext), []);\n\n const sensors = useSensors(useSensor(PointerSensor), useSensor(KeyboardSensor, { coordinateGetter }));\n\n // where is the activeItem being positioned (depth and parent)\n const projected = useMemo(\n () =>\n over && active\n ? getProjection(\n visibleFlattenedItems,\n flattenedItemsDictionary,\n over,\n dropIndicatorPosition,\n isExpandable,\n active,\n )\n : null,\n [over, visibleFlattenedItems, flattenedItemsDictionary, dropIndicatorPosition, isExpandable, active],\n );\n\n const dragPreviewHandlers = useTreePreviewHandlers({\n setActive,\n setOver,\n setDropIndicatorPosition,\n });\n\n const dragActionHandlers = useTreeActionHandlers({\n ...dragPreviewHandlers,\n onReorder,\n projected,\n flattenedItems,\n dropIndicatorPosition,\n isDropValid,\n flattenedItemsDictionary,\n containersRef,\n });\n\n const announcements = useTreeAnnouncements({\n flattenedItems,\n visibleFlattenedItems,\n flattenedItemsDictionary,\n containersRef,\n dropIndicatorPosition,\n latestDropIndicatorPositionRef,\n isExpandable,\n containerNodesRef,\n });\n\n const rawCollisionDetection = useMemo(() => customCollisionDetection(sensorContext), []);\n // Capture the fresh drop-indicator position each time collisions are computed, so announcements can\n // read it without waiting for the lagging `dropIndicatorPosition` state to commit.\n const collisionDetection = useCallback<CollisionDetection>(\n (collisionArgs) => {\n const collisions = rawCollisionDetection(collisionArgs);\n const fresh = (collisions?.[0]?.data as DnDKitTree.DragEventData | undefined)?.dropIndicatorPosition;\n if (fresh !== undefined) latestDropIndicatorPositionRef.current = fresh;\n return collisions;\n },\n [rawCollisionDetection],\n );\n\n const dndContextProps = useMemo(\n () => ({\n accessibility: {\n announcements,\n container: document.body,\n },\n modifiers,\n sensors,\n measuring,\n collisionDetection,\n ...dragActionHandlers,\n }),\n [announcements, modifiers, sensors, collisionDetection, dragActionHandlers],\n );\n\n const sortableContextProps = useMemo(\n () =>\n Object.keys(sortedIds).reduce(\n (acc, curr) => {\n acc[curr] = {\n items: sortedIds[curr],\n strategy: () => null,\n };\n return acc;\n },\n {} as Record<string, DnDKitTree.SortableContextPropsType>,\n ),\n [sortedIds],\n );\n\n const activeItem = useMemo(() => {\n if (!active) return null;\n return flattenedItemsDictionary[active.id];\n }, [active, flattenedItemsDictionary]);\n\n const overItem = useMemo(() => {\n if (!over) return null;\n return flattenedItemsDictionary[over.id];\n }, [flattenedItemsDictionary, over]);\n\n return {\n dndContextProps,\n sortableContextProps,\n isDropValid,\n active,\n activeItem,\n over,\n overItem,\n dropIndicatorPosition,\n containerNodesRef,\n };\n};\n", "import * as React from 'react';\nexport { React };\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -37,11 +37,18 @@ var import_constants = require("./constants.js");
37
37
  var import_utilities = require("./utilities.js");
38
38
  const useTreeAnnouncements = ({
39
39
  flattenedItems,
40
+ visibleFlattenedItems,
40
41
  flattenedItemsDictionary,
41
42
  containersRef,
42
43
  dropIndicatorPosition,
44
+ latestDropIndicatorPositionRef,
45
+ isExpandable,
43
46
  containerNodesRef
44
47
  }) => {
48
+ const getActiveDropIndicatorPosition = (0, import_react.useCallback)(
49
+ () => latestDropIndicatorPositionRef?.current ?? dropIndicatorPosition,
50
+ [latestDropIndicatorPositionRef, dropIndicatorPosition]
51
+ );
45
52
  const announcedContainerRef = (0, import_react.useRef)(null);
46
53
  const hasMovedRef = (0, import_react.useRef)(false);
47
54
  const isStillAtOrigin = (0, import_react.useCallback)(
@@ -54,19 +61,35 @@ const useTreeAnnouncements = ({
54
61
  );
55
62
  const getMoveContext = (0, import_react.useCallback)(
56
63
  (active, over) => {
57
- const { isOverContainerOfContainer, overContainer, activeContainer, overIndex } = (0, import_utilities.getDropLandingContext)(
64
+ const activeDropIndicatorPosition = getActiveDropIndicatorPosition();
65
+ const { isOverContainerOfContainer, overContainer, activeContainer } = (0, import_utilities.getDropLandingContext)(
58
66
  active,
59
67
  over,
60
- dropIndicatorPosition,
68
+ activeDropIndicatorPosition,
61
69
  flattenedItems,
62
70
  flattenedItemsDictionary,
63
71
  containersRef
64
72
  );
65
- const rawPosition = isOverContainerOfContainer ? (flattenedItems[overContainer]?.length ?? 0) + 1 : overIndex + 1;
73
+ const projection = (0, import_utilities.getProjection)(
74
+ visibleFlattenedItems,
75
+ flattenedItemsDictionary,
76
+ over,
77
+ activeDropIndicatorPosition,
78
+ isExpandable,
79
+ active
80
+ );
81
+ const rawPosition = isOverContainerOfContainer || projection?.position === void 0 ? (flattenedItems[overContainer]?.length ?? 0) + 1 : projection.position + 1;
66
82
  if (announcedContainerRef.current === null) announcedContainerRef.current = String(activeContainer);
67
83
  return { position: Math.max(1, rawPosition), overContainer: String(overContainer) };
68
84
  },
69
- [dropIndicatorPosition, flattenedItems, flattenedItemsDictionary, containersRef]
85
+ [
86
+ getActiveDropIndicatorPosition,
87
+ flattenedItems,
88
+ visibleFlattenedItems,
89
+ isExpandable,
90
+ flattenedItemsDictionary,
91
+ containersRef
92
+ ]
70
93
  );
71
94
  const onDragStart = (0, import_react.useCallback)(
72
95
  ({ active: { id } }) => {
@@ -80,7 +103,7 @@ const useTreeAnnouncements = ({
80
103
  ({ active, over }) => {
81
104
  if (!over) return `Draggable item is no longer over a droppable area.`;
82
105
  if (over.id === active.id) return "";
83
- if (dropIndicatorPosition === import_constants.DropIndicatorPosition.Inside && flattenedItemsDictionary[over.id]) {
106
+ if (getActiveDropIndicatorPosition() === import_constants.DropIndicatorPosition.Inside && flattenedItemsDictionary[over.id]) {
84
107
  return `Draggable item was moved inside the item at position ${flattenedItemsDictionary[over.id].realIndex + 1}.`;
85
108
  }
86
109
  const { position, overContainer } = getMoveContext(active, over);
@@ -88,13 +111,13 @@ const useTreeAnnouncements = ({
88
111
  hasMovedRef.current = true;
89
112
  return `Draggable item was moved to position ${position}.`;
90
113
  },
91
- [dropIndicatorPosition, flattenedItemsDictionary, getMoveContext, isStillAtOrigin]
114
+ [getActiveDropIndicatorPosition, flattenedItemsDictionary, getMoveContext, isStillAtOrigin]
92
115
  );
93
116
  const onDragOver = (0, import_react.useCallback)(
94
117
  ({ active, over }) => {
95
118
  if (!over) return `Draggable item is no longer over a droppable area.`;
96
119
  if (over.id === active.id) return "";
97
- if (dropIndicatorPosition === import_constants.DropIndicatorPosition.Inside && flattenedItemsDictionary[over.id]) {
120
+ if (getActiveDropIndicatorPosition() === import_constants.DropIndicatorPosition.Inside && flattenedItemsDictionary[over.id]) {
98
121
  return `Draggable item was moved inside the item at position ${flattenedItemsDictionary[over.id].realIndex + 1}.`;
99
122
  }
100
123
  const { position, overContainer } = getMoveContext(active, over);
@@ -108,14 +131,14 @@ const useTreeAnnouncements = ({
108
131
  }
109
132
  return `Draggable item was moved to position ${position}.`;
110
133
  },
111
- [dropIndicatorPosition, flattenedItemsDictionary, getMoveContext, containerNodesRef, isStillAtOrigin]
134
+ [getActiveDropIndicatorPosition, flattenedItemsDictionary, getMoveContext, containerNodesRef, isStillAtOrigin]
112
135
  );
113
136
  const onDragEnd = (0, import_react.useCallback)(
114
137
  ({ active, over }) => {
115
138
  announcedContainerRef.current = null;
116
139
  hasMovedRef.current = false;
117
140
  if (!over || over.id === active.id) return `Draggable item was dropped at it's original position.`;
118
- if (dropIndicatorPosition === import_constants.DropIndicatorPosition.Inside && flattenedItemsDictionary[over.id]) {
141
+ if (getActiveDropIndicatorPosition() === import_constants.DropIndicatorPosition.Inside && flattenedItemsDictionary[over.id]) {
119
142
  return `Draggable item was dropped inside the item at position ${flattenedItemsDictionary[over.id].realIndex + 1}.`;
120
143
  }
121
144
  const { position, overContainer } = getMoveContext(active, over);
@@ -126,7 +149,7 @@ const useTreeAnnouncements = ({
126
149
  }
127
150
  return `Draggable item was dropped at position ${position}.`;
128
151
  },
129
- [dropIndicatorPosition, flattenedItemsDictionary, getMoveContext, containerNodesRef]
152
+ [getActiveDropIndicatorPosition, flattenedItemsDictionary, getMoveContext, containerNodesRef]
130
153
  );
131
154
  const onDragCancel = (0, import_react.useCallback)(
132
155
  ({ active: { id } }) => {
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/tree/useTreeAnnouncements.tsx", "../../../../../../scripts/build/transpile/react-shim.js"],
4
- "sourcesContent": ["import type { Announcements, UniqueIdentifier, Active, Over } from '@dnd-kit/core';\nimport { useCallback, useRef } from 'react';\nimport { DropIndicatorPosition } from './constants.js';\nimport type { DnDKitTree } from './types.js';\nimport { getDropLandingContext, resolveAccessibleName } from './utilities.js';\n\n/**\n * Screen-reader announcements for the tree DnD engine (shared by single- and multi-tree configs).\n *\n * Position: the announced position is the real 1-based landing slot, derived from the same\n * `getDropLandingContext` the reorder (`onDragEnd`) uses \u2014 so the spoken number matches where the item\n * lands, and never underflows to 0.\n *\n * Destination container (name-on-entry): the destination container is named only when the drag *enters*\n * a different container, not on every move within it. dnd-kit fires `onDragOver` exactly when the over\n * target changes (a container crossing is such a change) and `onDragMove` on every coordinate move; so\n * `onDragOver` owns the entry naming (compared against `announcedContainerRef`) while `onDragMove` stays\n * position-only. The name is resolved from the container's accessible name (`aria-label` /\n * `aria-labelledby`), with a generic fallback when unlabeled.\n *\n * Pickup vs move: on pickup, dnd-kit fires `onDragStart` (\"Picked up \u2026 position N\") and then a settle\n * `onDragOver`/`onDragMove`. For a non-top item that settle resolves `over` to a neighbour (not the item\n * itself), which would emit a spurious \"moved to position N\" that overwrites the pickup message. To\n * prevent that, move announcements are suppressed until the item has *genuinely* moved \u2014 i.e. its landing\n * position or container differs from its origin (`hasMovedRef` latch). The latch also lets a later return\n * to the origin position be announced normally.\n */\nexport type UseTreeAnnouncementsArgs<T> = {\n flattenedItems: Record<string, DnDKitTree.Item<T>[]>;\n flattenedItemsDictionary: Record<UniqueIdentifier, DnDKitTree.Item<T>>;\n containersRef: { current: UniqueIdentifier[] };\n dropIndicatorPosition: DnDKitTree.DropIndicatorPositionValues;\n containerNodesRef: { current: Record<string, HTMLElement | null> };\n};\n\nexport const useTreeAnnouncements = <T,>({\n flattenedItems,\n flattenedItemsDictionary,\n containersRef,\n dropIndicatorPosition,\n containerNodesRef,\n}: UseTreeAnnouncementsArgs<T>): Announcements => {\n // The container the user was last told about (the drag origin, then each container they enter).\n // null = not yet initialised for this drag; lazily seeded to the active item's origin container.\n const announcedContainerRef = useRef<string | null>(null);\n // Latches true once the item has genuinely moved from its origin \u2014 suppresses the pickup-settle\n // \"moved\" announcement that would otherwise overwrite the \"Picked up \u2026\" message.\n const hasMovedRef = useRef(false);\n\n // Whether the current landing target is still the item's origin slot (same position AND container).\n // active.realIndex/container reflect the origin throughout the drag (data only mutates on drop).\n const isStillAtOrigin = useCallback(\n (active: Active, position: number, overContainer: string) => {\n const originPosition = (flattenedItemsDictionary[active.id]?.realIndex ?? 0) + 1;\n const originContainer = String(flattenedItemsDictionary[active.id]?.container ?? 'root');\n return position === originPosition && overContainer === originContainer;\n },\n [flattenedItemsDictionary],\n );\n\n const getMoveContext = useCallback(\n (active: Active, over: Over) => {\n const { isOverContainerOfContainer, overContainer, activeContainer, overIndex } = getDropLandingContext(\n active,\n over,\n dropIndicatorPosition,\n flattenedItems,\n flattenedItemsDictionary,\n containersRef,\n );\n const rawPosition = isOverContainerOfContainer ? (flattenedItems[overContainer]?.length ?? 0) + 1 : overIndex + 1;\n if (announcedContainerRef.current === null) announcedContainerRef.current = String(activeContainer);\n return { position: Math.max(1, rawPosition), overContainer: String(overContainer) };\n },\n [dropIndicatorPosition, flattenedItems, flattenedItemsDictionary, containersRef],\n );\n\n const onDragStart: Announcements['onDragStart'] = useCallback(\n ({ active: { id } }) => {\n announcedContainerRef.current = String(flattenedItemsDictionary[id]?.container ?? 'root');\n hasMovedRef.current = false;\n return `Picked up draggable item from position ${(flattenedItemsDictionary[id]?.realIndex ?? 0) + 1}.`;\n },\n [flattenedItemsDictionary],\n );\n\n // onDragMove: position-only, never names the container (fires on every coordinate move).\n // Typed via the non-optional onDragOver signature (same event shape) to keep params strongly typed.\n const onDragMove: Announcements['onDragOver'] = useCallback(\n ({ active, over }) => {\n if (!over) return `Draggable item is no longer over a droppable area.`;\n if (over.id === active.id) return ''; // over itself (e.g. the pickup frame) \u2014 no real move\n if (dropIndicatorPosition === DropIndicatorPosition.Inside && flattenedItemsDictionary[over.id]) {\n return `Draggable item was moved inside the item at position ${flattenedItemsDictionary[over.id].realIndex + 1}.`;\n }\n const { position, overContainer } = getMoveContext(active, over);\n // Suppress the pickup-settle \"moved\" until the item genuinely leaves its origin slot.\n if (!hasMovedRef.current && isStillAtOrigin(active, position, overContainer)) return '';\n hasMovedRef.current = true;\n return `Draggable item was moved to position ${position}.`;\n },\n [dropIndicatorPosition, flattenedItemsDictionary, getMoveContext, isStillAtOrigin],\n );\n\n // onDragOver: fires when the over target changes; names the destination container on entry.\n const onDragOver: Announcements['onDragOver'] = useCallback(\n ({ active, over }) => {\n if (!over) return `Draggable item is no longer over a droppable area.`;\n if (over.id === active.id) return '';\n if (dropIndicatorPosition === DropIndicatorPosition.Inside && flattenedItemsDictionary[over.id]) {\n return `Draggable item was moved inside the item at position ${flattenedItemsDictionary[over.id].realIndex + 1}.`;\n }\n const { position, overContainer } = getMoveContext(active, over);\n // Suppress the pickup-settle \"moved\" until the item genuinely leaves its origin slot.\n if (!hasMovedRef.current && isStillAtOrigin(active, position, overContainer)) return '';\n hasMovedRef.current = true;\n const isEntry = overContainer !== announcedContainerRef.current;\n announcedContainerRef.current = overContainer;\n if (isEntry) {\n const destName = resolveAccessibleName(containerNodesRef.current[overContainer]);\n return `Draggable item was moved to ${destName ?? 'another container'}, position ${position}.`;\n }\n return `Draggable item was moved to position ${position}.`;\n },\n [dropIndicatorPosition, flattenedItemsDictionary, getMoveContext, containerNodesRef, isStillAtOrigin],\n );\n\n const onDragEnd: Announcements['onDragEnd'] = useCallback(\n ({ active, over }) => {\n announcedContainerRef.current = null; // reset for the next drag\n hasMovedRef.current = false;\n if (!over || over.id === active.id) return `Draggable item was dropped at it's original position.`;\n if (dropIndicatorPosition === DropIndicatorPosition.Inside && flattenedItemsDictionary[over.id]) {\n return `Draggable item was dropped inside the item at position ${\n flattenedItemsDictionary[over.id].realIndex + 1\n }.`;\n }\n const { position, overContainer } = getMoveContext(active, over);\n const activeContainer = String(flattenedItemsDictionary[active.id]?.container ?? 'root');\n // Name the destination only when the item is dropped into a different container than it started in.\n if (overContainer !== activeContainer) {\n const destName = resolveAccessibleName(containerNodesRef.current[overContainer]);\n return `Draggable item was dropped in ${destName ?? 'another container'}, position ${position}.`;\n }\n return `Draggable item was dropped at position ${position}.`;\n },\n [dropIndicatorPosition, flattenedItemsDictionary, getMoveContext, containerNodesRef],\n );\n\n const onDragCancel: Announcements['onDragCancel'] = useCallback(\n ({ active: { id } }) => {\n announcedContainerRef.current = null;\n hasMovedRef.current = false;\n return `Dragging was cancelled. Draggable item from position ${\n (flattenedItemsDictionary[id]?.realIndex ?? 0) + 1\n } was dropped at it's initial position.`;\n },\n [flattenedItemsDictionary],\n );\n\n return { onDragStart, onDragOver, onDragMove, onDragEnd, onDragCancel };\n};\n", "import * as React from 'react';\nexport { React };\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import type { Announcements, UniqueIdentifier, Active, Over } from '@dnd-kit/core';\nimport { useCallback, useRef } from 'react';\nimport { DropIndicatorPosition } from './constants.js';\nimport type { DnDKitTree } from './types.js';\nimport { getDropLandingContext, getProjection, resolveAccessibleName } from './utilities.js';\n\n/**\n * Screen-reader announcements for the tree DnD engine (shared by single- and multi-tree configs).\n *\n * Position: the announced position is the 1-based slot shown by the visible drop indicator, derived from\n * the same `getProjection` that positions that indicator (and feeds the consumer's `onReorder`). Sourcing\n * both from one projection makes screen-reader/sighted parity structural \u2014 the spoken number cannot drift\n * from the indicator, including across containers \u2014 and it never underflows to 0. Container-of-container\n * targets (dropping onto an empty/whole container) have no projected slot and append to the end.\n *\n * Destination container (name-on-entry): the destination container is named only when the drag *enters*\n * a different container, not on every move within it. dnd-kit fires `onDragOver` exactly when the over\n * target changes (a container crossing is such a change) and `onDragMove` on every coordinate move; so\n * `onDragOver` owns the entry naming (compared against `announcedContainerRef`) while `onDragMove` stays\n * position-only. The name is resolved from the container's accessible name (`aria-label` /\n * `aria-labelledby`), with a generic fallback when unlabeled.\n *\n * Pickup vs move: on pickup, dnd-kit fires `onDragStart` (\"Picked up \u2026 position N\") and then a settle\n * `onDragOver`/`onDragMove`. For a non-top item that settle resolves `over` to a neighbour (not the item\n * itself), which would emit a spurious \"moved to position N\" that overwrites the pickup message. To\n * prevent that, move announcements are suppressed until the item has *genuinely* moved \u2014 i.e. its landing\n * position or container differs from its origin (`hasMovedRef` latch). The latch also lets a later return\n * to the origin position be announced normally.\n */\nexport type UseTreeAnnouncementsArgs<T> = {\n flattenedItems: Record<string, DnDKitTree.Item<T>[]>;\n // Items with the active item's descendants removed \u2014 the exact list `getProjection` projects against\n // to position the drop indicator. Announcements read the same list so the spoken slot mirrors it.\n visibleFlattenedItems: Record<string, DnDKitTree.Item<T>[]>;\n flattenedItemsDictionary: Record<UniqueIdentifier, DnDKitTree.Item<T>>;\n containersRef: { current: UniqueIdentifier[] };\n dropIndicatorPosition: DnDKitTree.DropIndicatorPositionValues;\n // Fresh drop-indicator position from the latest collision computation. Optional: when absent (e.g. unit\n // tests that drive the handlers synchronously), the `dropIndicatorPosition` prop is the source of truth.\n // When present, it is preferred because the prop lags by one render during a live drag (see the config).\n latestDropIndicatorPositionRef?: { current: DnDKitTree.DropIndicatorPositionValues };\n isExpandable: boolean;\n containerNodesRef: { current: Record<string, HTMLElement | null> };\n};\n\nexport const useTreeAnnouncements = <T,>({\n flattenedItems,\n visibleFlattenedItems,\n flattenedItemsDictionary,\n containersRef,\n dropIndicatorPosition,\n latestDropIndicatorPositionRef,\n isExpandable,\n containerNodesRef,\n}: UseTreeAnnouncementsArgs<T>): Announcements => {\n // Prefer the fresh collision-time position; fall back to the prop when no ref is provided.\n const getActiveDropIndicatorPosition = useCallback(\n () => latestDropIndicatorPositionRef?.current ?? dropIndicatorPosition,\n [latestDropIndicatorPositionRef, dropIndicatorPosition],\n );\n // The container the user was last told about (the drag origin, then each container they enter).\n // null = not yet initialised for this drag; lazily seeded to the active item's origin container.\n const announcedContainerRef = useRef<string | null>(null);\n // Latches true once the item has genuinely moved from its origin \u2014 suppresses the pickup-settle\n // \"moved\" announcement that would otherwise overwrite the \"Picked up \u2026\" message.\n const hasMovedRef = useRef(false);\n\n // Whether the current landing target is still the item's origin slot (same position AND container).\n // active.realIndex/container reflect the origin throughout the drag (data only mutates on drop).\n const isStillAtOrigin = useCallback(\n (active: Active, position: number, overContainer: string) => {\n const originPosition = (flattenedItemsDictionary[active.id]?.realIndex ?? 0) + 1;\n const originContainer = String(flattenedItemsDictionary[active.id]?.container ?? 'root');\n return position === originPosition && overContainer === originContainer;\n },\n [flattenedItemsDictionary],\n );\n\n const getMoveContext = useCallback(\n (active: Active, over: Over) => {\n const activeDropIndicatorPosition = getActiveDropIndicatorPosition();\n // Destination container (for entry detection + accessible-name resolution) stays on the landing-context\n // path; only the numeric position is sourced from the projection so it mirrors the visible indicator.\n const { isOverContainerOfContainer, overContainer, activeContainer } = getDropLandingContext(\n active,\n over,\n activeDropIndicatorPosition,\n flattenedItems,\n flattenedItemsDictionary,\n containersRef,\n );\n const projection = getProjection(\n visibleFlattenedItems,\n flattenedItemsDictionary,\n over,\n activeDropIndicatorPosition,\n isExpandable,\n active,\n );\n // Container-of-container targets have no projected slot \u2014 append to the container's end (as before).\n const rawPosition =\n isOverContainerOfContainer || projection?.position === undefined\n ? (flattenedItems[overContainer]?.length ?? 0) + 1\n : projection.position + 1;\n if (announcedContainerRef.current === null) announcedContainerRef.current = String(activeContainer);\n return { position: Math.max(1, rawPosition), overContainer: String(overContainer) };\n },\n [\n getActiveDropIndicatorPosition,\n flattenedItems,\n visibleFlattenedItems,\n isExpandable,\n flattenedItemsDictionary,\n containersRef,\n ],\n );\n\n const onDragStart: Announcements['onDragStart'] = useCallback(\n ({ active: { id } }) => {\n announcedContainerRef.current = String(flattenedItemsDictionary[id]?.container ?? 'root');\n hasMovedRef.current = false;\n return `Picked up draggable item from position ${(flattenedItemsDictionary[id]?.realIndex ?? 0) + 1}.`;\n },\n [flattenedItemsDictionary],\n );\n\n // onDragMove: position-only, never names the container (fires on every coordinate move).\n // Typed via the non-optional onDragOver signature (same event shape) to keep params strongly typed.\n const onDragMove: Announcements['onDragOver'] = useCallback(\n ({ active, over }) => {\n if (!over) return `Draggable item is no longer over a droppable area.`;\n if (over.id === active.id) return ''; // over itself (e.g. the pickup frame) \u2014 no real move\n if (getActiveDropIndicatorPosition() === DropIndicatorPosition.Inside && flattenedItemsDictionary[over.id]) {\n return `Draggable item was moved inside the item at position ${flattenedItemsDictionary[over.id].realIndex + 1}.`;\n }\n const { position, overContainer } = getMoveContext(active, over);\n // Suppress the pickup-settle \"moved\" until the item genuinely leaves its origin slot.\n if (!hasMovedRef.current && isStillAtOrigin(active, position, overContainer)) return '';\n hasMovedRef.current = true;\n return `Draggable item was moved to position ${position}.`;\n },\n [getActiveDropIndicatorPosition, flattenedItemsDictionary, getMoveContext, isStillAtOrigin],\n );\n\n // onDragOver: fires when the over target changes; names the destination container on entry.\n const onDragOver: Announcements['onDragOver'] = useCallback(\n ({ active, over }) => {\n if (!over) return `Draggable item is no longer over a droppable area.`;\n if (over.id === active.id) return '';\n if (getActiveDropIndicatorPosition() === DropIndicatorPosition.Inside && flattenedItemsDictionary[over.id]) {\n return `Draggable item was moved inside the item at position ${flattenedItemsDictionary[over.id].realIndex + 1}.`;\n }\n const { position, overContainer } = getMoveContext(active, over);\n // Suppress the pickup-settle \"moved\" until the item genuinely leaves its origin slot.\n if (!hasMovedRef.current && isStillAtOrigin(active, position, overContainer)) return '';\n hasMovedRef.current = true;\n const isEntry = overContainer !== announcedContainerRef.current;\n announcedContainerRef.current = overContainer;\n if (isEntry) {\n const destName = resolveAccessibleName(containerNodesRef.current[overContainer]);\n return `Draggable item was moved to ${destName ?? 'another container'}, position ${position}.`;\n }\n return `Draggable item was moved to position ${position}.`;\n },\n [getActiveDropIndicatorPosition, flattenedItemsDictionary, getMoveContext, containerNodesRef, isStillAtOrigin],\n );\n\n const onDragEnd: Announcements['onDragEnd'] = useCallback(\n ({ active, over }) => {\n announcedContainerRef.current = null; // reset for the next drag\n hasMovedRef.current = false;\n if (!over || over.id === active.id) return `Draggable item was dropped at it's original position.`;\n if (getActiveDropIndicatorPosition() === DropIndicatorPosition.Inside && flattenedItemsDictionary[over.id]) {\n return `Draggable item was dropped inside the item at position ${\n flattenedItemsDictionary[over.id].realIndex + 1\n }.`;\n }\n const { position, overContainer } = getMoveContext(active, over);\n const activeContainer = String(flattenedItemsDictionary[active.id]?.container ?? 'root');\n // Name the destination only when the item is dropped into a different container than it started in.\n if (overContainer !== activeContainer) {\n const destName = resolveAccessibleName(containerNodesRef.current[overContainer]);\n return `Draggable item was dropped in ${destName ?? 'another container'}, position ${position}.`;\n }\n return `Draggable item was dropped at position ${position}.`;\n },\n [getActiveDropIndicatorPosition, flattenedItemsDictionary, getMoveContext, containerNodesRef],\n );\n\n const onDragCancel: Announcements['onDragCancel'] = useCallback(\n ({ active: { id } }) => {\n announcedContainerRef.current = null;\n hasMovedRef.current = false;\n return `Dragging was cancelled. Draggable item from position ${\n (flattenedItemsDictionary[id]?.realIndex ?? 0) + 1\n } was dropped at it's initial position.`;\n },\n [flattenedItemsDictionary],\n );\n\n return { onDragStart, onDragOver, onDragMove, onDragEnd, onDragCancel };\n};\n", "import * as React from 'react';\nexport { React };\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -1,5 +1,5 @@
1
1
  import * as React from "react";
2
- import { useState, useEffect, useMemo, useRef } from "react";
2
+ import { useState, useEffect, useMemo, useRef, useCallback } from "react";
3
3
  import { useSensor, useSensors, KeyboardSensor, PointerSensor, MeasuringStrategy } from "@dnd-kit/core";
4
4
  import { useTreePreviewHandlers } from "./useTreePreviewHandlers.js";
5
5
  import { getTreeKeyboardCoordinates } from "./getTreeKeyboardCoordinates.js";
@@ -58,6 +58,7 @@ const useMultiTreeDndkitConfig = ({
58
58
  );
59
59
  const containersRef = useRef([]);
60
60
  const containerNodesRef = useRef({});
61
+ const latestDropIndicatorPositionRef = useRef(dropIndicatorPosition);
61
62
  const visibleFlattenedItems = useMemo(() => {
62
63
  const dictionary = {};
63
64
  const collapsedItems = {};
@@ -147,12 +148,24 @@ const useMultiTreeDndkitConfig = ({
147
148
  });
148
149
  const announcements = useTreeAnnouncements({
149
150
  flattenedItems,
151
+ visibleFlattenedItems,
150
152
  flattenedItemsDictionary,
151
153
  containersRef,
152
154
  dropIndicatorPosition,
155
+ latestDropIndicatorPositionRef,
156
+ isExpandable,
153
157
  containerNodesRef
154
158
  });
155
- const collisionDetection = customCollisionDetection(sensorContext);
159
+ const rawCollisionDetection = useMemo(() => customCollisionDetection(sensorContext), []);
160
+ const collisionDetection = useCallback(
161
+ (collisionArgs) => {
162
+ const collisions = rawCollisionDetection(collisionArgs);
163
+ const fresh = collisions?.[0]?.data?.dropIndicatorPosition;
164
+ if (fresh !== void 0) latestDropIndicatorPositionRef.current = fresh;
165
+ return collisions;
166
+ },
167
+ [rawCollisionDetection]
168
+ );
156
169
  const dndContextProps = useMemo(
157
170
  () => ({
158
171
  accessibility: {
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../../scripts/build/transpile/react-shim.js", "../../../src/tree/useMultiTreeDndkitConfig.tsx"],
4
- "sourcesContent": ["import * as React from 'react';\nexport { React };\n", "/* eslint-disable max-statements */\n/* eslint-disable max-lines */\nimport { useState, useEffect, useMemo, useRef } from 'react';\nimport { type TypescriptHelpersT } from '@elliemae/ds-typescript-helpers';\nimport type { Active, MeasuringConfiguration, Modifier, Over, UniqueIdentifier } from '@dnd-kit/core';\nimport { useSensor, useSensors, KeyboardSensor, PointerSensor, MeasuringStrategy } from '@dnd-kit/core';\nimport { useTreePreviewHandlers } from './useTreePreviewHandlers.js';\nimport { getTreeKeyboardCoordinates } from './getTreeKeyboardCoordinates.js';\nimport { getProjection, removeChildrenOf } from './utilities.js';\nimport { useTreeActionHandlers } from './useTreeActionHandlers.js';\nimport type { DnDKitTree } from './types.js';\nimport { DropIndicatorPosition } from './constants.js';\nimport { customCollisionDetection } from './customCollisionDetection.js';\nimport { useTreeAnnouncements } from './useTreeAnnouncements.js';\n\n// we make space for the drop indicator\n// if second parameter is true, the space will be done on the horizontal axis\nexport const adjustTranslate = (isHorizontalDnD: boolean, sortedIds: Record<string, UniqueIdentifier[]>): Modifier => {\n const func: Modifier = ({ transform, activatorEvent, active }) => {\n const newTransform = {\n ...transform,\n };\n if (isHorizontalDnD) {\n newTransform.x = transform.x + 25;\n } else {\n newTransform.x = transform.x + 15;\n if (['Enter', 'Space'].includes((activatorEvent as KeyboardEvent)?.code)) {\n const activeContainerId =\n Object.keys(sortedIds).find((key) => sortedIds[key].includes(active?.id ?? '')) ?? Object.keys(sortedIds)[0];\n const isLast = active?.id === sortedIds[activeContainerId][sortedIds[activeContainerId].length - 1];\n const keyboardTranslate = ((isLast ? -1 : 1) * (active?.rect?.current?.initial?.height ?? 0)) / 2;\n newTransform.y = transform.y + keyboardTranslate;\n }\n }\n return newTransform;\n };\n return func;\n};\n\nconst measuring: Partial<MeasuringConfiguration> = {\n droppable: {\n strategy: MeasuringStrategy.Always,\n },\n};\n\nexport const useMultiTreeDndkitConfig = <T,>({\n flattenedItems,\n onReorder,\n isHorizontalDnD = false,\n isExpandable = false,\n getIsDropValid = () => true,\n maxDragAndDropLevel = Infinity,\n}: DnDKitTree.UseMultiTreeConfigArgs<T>): DnDKitTree.UseTreeConfigReturn<T> => {\n const [active, setActive] = useState<Active | null>(null);\n const [over, setOver] = useState<Over | null>(null);\n const [dropIndicatorPosition, setDropIndicatorPosition] = useState<DnDKitTree.DropIndicatorPositionValues>(\n DropIndicatorPosition.After,\n );\n // Sorted ids for the library\n const sortedIds = useMemo(\n () =>\n Object.keys(flattenedItems).reduce(\n (acc, curr) => {\n acc[curr] = flattenedItems[curr].map((item) => item.uid);\n return acc;\n },\n {} as Record<string, UniqueIdentifier[]>,\n ),\n [flattenedItems],\n );\n\n /**\n * List of unique containers\n */\n const containersRef = useRef([] as string[]);\n\n /**\n * Container id -> DOM node, registered by DSSortableContainer. Used to resolve the destination\n * container's accessible name for screen-reader announcements on cross-container moves.\n */\n const containerNodesRef = useRef({} as Record<string, HTMLElement | null>);\n\n const visibleFlattenedItems = useMemo(() => {\n const dictionary: Record<string, DnDKitTree.Item<T>[]> = {};\n const collapsedItems: Record<UniqueIdentifier, boolean | undefined> = {};\n Object.keys(flattenedItems).forEach((container) => {\n const itemWithNoActiveChildren = removeChildrenOf(flattenedItems[container], active?.id);\n\n dictionary[container] = [];\n\n itemWithNoActiveChildren.forEach((item) => {\n if (item.parentId === null || !collapsedItems[item.parentId]) {\n dictionary[container].push(item);\n }\n collapsedItems[item.uid] = item.collapsed;\n });\n });\n return dictionary;\n }, [active?.id, flattenedItems]);\n\n /**\n * Dictionary from UID to ITEM\n * This dictionary is computed since on every DnD move, I need to know the\n * depth of a particular item, so O(1) per DnD move instead of O(#ITEMS)\n */\n const flattenedItemsDictionary = useMemo(() => {\n const dictionary: Record<UniqueIdentifier, TypescriptHelpersT.ObjectValues<typeof flattenedItems>[0]> = {};\n Object.keys(flattenedItems).forEach((container) => {\n flattenedItems[container].forEach((item) => {\n dictionary[item.uid] = item;\n if (!item.container) return;\n if (!containersRef.current.includes(item.container)) containersRef.current.push(item.container);\n });\n });\n return dictionary;\n }, [flattenedItems]);\n\n const isDropValid = useMemo(() => {\n if (!active || !over) return true;\n return getIsDropValid(\n flattenedItemsDictionary[active.id],\n flattenedItemsDictionary[over.id],\n dropIndicatorPosition,\n );\n }, [getIsDropValid, flattenedItemsDictionary, active, over, dropIndicatorPosition]);\n\n const modifiers: Modifier[] = useMemo(\n () => [adjustTranslate(isHorizontalDnD, sortedIds)],\n [isHorizontalDnD, sortedIds],\n );\n\n const sensorContext: DnDKitTree.SensorContext<T> = useRef({\n items: flattenedItems,\n dropIndicatorPosition,\n setDropIndicatorPosition,\n isHorizontalDnD,\n maxDragAndDropLevel,\n active,\n flattenedItemsDictionary,\n containersRef,\n });\n\n useEffect(() => {\n sensorContext.current = {\n items: flattenedItems,\n dropIndicatorPosition,\n isHorizontalDnD,\n maxDragAndDropLevel,\n active,\n flattenedItemsDictionary,\n containersRef,\n };\n }, [flattenedItems, dropIndicatorPosition, isHorizontalDnD, maxDragAndDropLevel, active, flattenedItemsDictionary]);\n\n const coordinateGetter = useMemo(() => getTreeKeyboardCoordinates(sensorContext), []);\n\n const sensors = useSensors(useSensor(PointerSensor), useSensor(KeyboardSensor, { coordinateGetter }));\n\n // where is the activeItem being positioned (depth and parent)\n const projected = useMemo(\n () =>\n over && active\n ? getProjection(\n visibleFlattenedItems,\n flattenedItemsDictionary,\n over,\n dropIndicatorPosition,\n isExpandable,\n active,\n )\n : null,\n [over, visibleFlattenedItems, flattenedItemsDictionary, dropIndicatorPosition, isExpandable, active],\n );\n\n const dragPreviewHandlers = useTreePreviewHandlers({\n setActive,\n setOver,\n setDropIndicatorPosition,\n });\n\n const dragActionHandlers = useTreeActionHandlers({\n ...dragPreviewHandlers,\n onReorder,\n projected,\n flattenedItems,\n dropIndicatorPosition,\n isDropValid,\n flattenedItemsDictionary,\n containersRef,\n });\n\n const announcements = useTreeAnnouncements({\n flattenedItems,\n flattenedItemsDictionary,\n containersRef,\n dropIndicatorPosition,\n containerNodesRef,\n });\n\n const collisionDetection = customCollisionDetection(sensorContext);\n\n const dndContextProps = useMemo(\n () => ({\n accessibility: {\n announcements,\n container: document.body,\n },\n modifiers,\n sensors,\n measuring,\n collisionDetection,\n ...dragActionHandlers,\n }),\n [announcements, modifiers, sensors, collisionDetection, dragActionHandlers],\n );\n\n const sortableContextProps = useMemo(\n () =>\n Object.keys(sortedIds).reduce(\n (acc, curr) => {\n acc[curr] = {\n items: sortedIds[curr],\n strategy: () => null,\n };\n return acc;\n },\n {} as Record<string, DnDKitTree.SortableContextPropsType>,\n ),\n [sortedIds],\n );\n\n const activeItem = useMemo(() => {\n if (!active) return null;\n return flattenedItemsDictionary[active.id];\n }, [active, flattenedItemsDictionary]);\n\n const overItem = useMemo(() => {\n if (!over) return null;\n return flattenedItemsDictionary[over.id];\n }, [flattenedItemsDictionary, over]);\n\n return {\n dndContextProps,\n sortableContextProps,\n isDropValid,\n active,\n activeItem,\n over,\n overItem,\n dropIndicatorPosition,\n containerNodesRef,\n };\n};\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import * as React from 'react';\nexport { React };\n", "/* eslint-disable max-statements */\n/* eslint-disable max-lines */\nimport { useState, useEffect, useMemo, useRef, useCallback } from 'react';\nimport { type TypescriptHelpersT } from '@elliemae/ds-typescript-helpers';\nimport type {\n Active,\n CollisionDetection,\n MeasuringConfiguration,\n Modifier,\n Over,\n UniqueIdentifier,\n} from '@dnd-kit/core';\nimport { useSensor, useSensors, KeyboardSensor, PointerSensor, MeasuringStrategy } from '@dnd-kit/core';\nimport { useTreePreviewHandlers } from './useTreePreviewHandlers.js';\nimport { getTreeKeyboardCoordinates } from './getTreeKeyboardCoordinates.js';\nimport { getProjection, removeChildrenOf } from './utilities.js';\nimport { useTreeActionHandlers } from './useTreeActionHandlers.js';\nimport type { DnDKitTree } from './types.js';\nimport { DropIndicatorPosition } from './constants.js';\nimport { customCollisionDetection } from './customCollisionDetection.js';\nimport { useTreeAnnouncements } from './useTreeAnnouncements.js';\n\n// we make space for the drop indicator\n// if second parameter is true, the space will be done on the horizontal axis\nexport const adjustTranslate = (isHorizontalDnD: boolean, sortedIds: Record<string, UniqueIdentifier[]>): Modifier => {\n const func: Modifier = ({ transform, activatorEvent, active }) => {\n const newTransform = {\n ...transform,\n };\n if (isHorizontalDnD) {\n newTransform.x = transform.x + 25;\n } else {\n newTransform.x = transform.x + 15;\n if (['Enter', 'Space'].includes((activatorEvent as KeyboardEvent)?.code)) {\n const activeContainerId =\n Object.keys(sortedIds).find((key) => sortedIds[key].includes(active?.id ?? '')) ?? Object.keys(sortedIds)[0];\n const isLast = active?.id === sortedIds[activeContainerId][sortedIds[activeContainerId].length - 1];\n const keyboardTranslate = ((isLast ? -1 : 1) * (active?.rect?.current?.initial?.height ?? 0)) / 2;\n newTransform.y = transform.y + keyboardTranslate;\n }\n }\n return newTransform;\n };\n return func;\n};\n\nconst measuring: Partial<MeasuringConfiguration> = {\n droppable: {\n strategy: MeasuringStrategy.Always,\n },\n};\n\nexport const useMultiTreeDndkitConfig = <T,>({\n flattenedItems,\n onReorder,\n isHorizontalDnD = false,\n isExpandable = false,\n getIsDropValid = () => true,\n maxDragAndDropLevel = Infinity,\n}: DnDKitTree.UseMultiTreeConfigArgs<T>): DnDKitTree.UseTreeConfigReturn<T> => {\n const [active, setActive] = useState<Active | null>(null);\n const [over, setOver] = useState<Over | null>(null);\n const [dropIndicatorPosition, setDropIndicatorPosition] = useState<DnDKitTree.DropIndicatorPositionValues>(\n DropIndicatorPosition.After,\n );\n // Sorted ids for the library\n const sortedIds = useMemo(\n () =>\n Object.keys(flattenedItems).reduce(\n (acc, curr) => {\n acc[curr] = flattenedItems[curr].map((item) => item.uid);\n return acc;\n },\n {} as Record<string, UniqueIdentifier[]>,\n ),\n [flattenedItems],\n );\n\n /**\n * List of unique containers\n */\n const containersRef = useRef([] as string[]);\n\n /**\n * Container id -> DOM node, registered by DSSortableContainer. Used to resolve the destination\n * container's accessible name for screen-reader announcements on cross-container moves.\n */\n const containerNodesRef = useRef({} as Record<string, HTMLElement | null>);\n\n /**\n * The drop-indicator position from the *latest* collision computation. `dropIndicatorPosition` state\n * lags by one render, so announcements \u2014 which fire during the drag event, before that state commits \u2014\n * would read a stale position whenever the indicator side flips (e.g. crossing above the first item of\n * a container). Collision detection runs before the announcement for the same event, so it writes the\n * fresh value here for the announcement to read, keeping the spoken slot in lockstep with the indicator.\n */\n const latestDropIndicatorPositionRef = useRef<DnDKitTree.DropIndicatorPositionValues>(dropIndicatorPosition);\n\n const visibleFlattenedItems = useMemo(() => {\n const dictionary: Record<string, DnDKitTree.Item<T>[]> = {};\n const collapsedItems: Record<UniqueIdentifier, boolean | undefined> = {};\n Object.keys(flattenedItems).forEach((container) => {\n const itemWithNoActiveChildren = removeChildrenOf(flattenedItems[container], active?.id);\n\n dictionary[container] = [];\n\n itemWithNoActiveChildren.forEach((item) => {\n if (item.parentId === null || !collapsedItems[item.parentId]) {\n dictionary[container].push(item);\n }\n collapsedItems[item.uid] = item.collapsed;\n });\n });\n return dictionary;\n }, [active?.id, flattenedItems]);\n\n /**\n * Dictionary from UID to ITEM\n * This dictionary is computed since on every DnD move, I need to know the\n * depth of a particular item, so O(1) per DnD move instead of O(#ITEMS)\n */\n const flattenedItemsDictionary = useMemo(() => {\n const dictionary: Record<UniqueIdentifier, TypescriptHelpersT.ObjectValues<typeof flattenedItems>[0]> = {};\n Object.keys(flattenedItems).forEach((container) => {\n flattenedItems[container].forEach((item) => {\n dictionary[item.uid] = item;\n if (!item.container) return;\n if (!containersRef.current.includes(item.container)) containersRef.current.push(item.container);\n });\n });\n return dictionary;\n }, [flattenedItems]);\n\n const isDropValid = useMemo(() => {\n if (!active || !over) return true;\n return getIsDropValid(\n flattenedItemsDictionary[active.id],\n flattenedItemsDictionary[over.id],\n dropIndicatorPosition,\n );\n }, [getIsDropValid, flattenedItemsDictionary, active, over, dropIndicatorPosition]);\n\n const modifiers: Modifier[] = useMemo(\n () => [adjustTranslate(isHorizontalDnD, sortedIds)],\n [isHorizontalDnD, sortedIds],\n );\n\n const sensorContext: DnDKitTree.SensorContext<T> = useRef({\n items: flattenedItems,\n dropIndicatorPosition,\n setDropIndicatorPosition,\n isHorizontalDnD,\n maxDragAndDropLevel,\n active,\n flattenedItemsDictionary,\n containersRef,\n });\n\n useEffect(() => {\n sensorContext.current = {\n items: flattenedItems,\n dropIndicatorPosition,\n isHorizontalDnD,\n maxDragAndDropLevel,\n active,\n flattenedItemsDictionary,\n containersRef,\n };\n }, [flattenedItems, dropIndicatorPosition, isHorizontalDnD, maxDragAndDropLevel, active, flattenedItemsDictionary]);\n\n const coordinateGetter = useMemo(() => getTreeKeyboardCoordinates(sensorContext), []);\n\n const sensors = useSensors(useSensor(PointerSensor), useSensor(KeyboardSensor, { coordinateGetter }));\n\n // where is the activeItem being positioned (depth and parent)\n const projected = useMemo(\n () =>\n over && active\n ? getProjection(\n visibleFlattenedItems,\n flattenedItemsDictionary,\n over,\n dropIndicatorPosition,\n isExpandable,\n active,\n )\n : null,\n [over, visibleFlattenedItems, flattenedItemsDictionary, dropIndicatorPosition, isExpandable, active],\n );\n\n const dragPreviewHandlers = useTreePreviewHandlers({\n setActive,\n setOver,\n setDropIndicatorPosition,\n });\n\n const dragActionHandlers = useTreeActionHandlers({\n ...dragPreviewHandlers,\n onReorder,\n projected,\n flattenedItems,\n dropIndicatorPosition,\n isDropValid,\n flattenedItemsDictionary,\n containersRef,\n });\n\n const announcements = useTreeAnnouncements({\n flattenedItems,\n visibleFlattenedItems,\n flattenedItemsDictionary,\n containersRef,\n dropIndicatorPosition,\n latestDropIndicatorPositionRef,\n isExpandable,\n containerNodesRef,\n });\n\n const rawCollisionDetection = useMemo(() => customCollisionDetection(sensorContext), []);\n // Capture the fresh drop-indicator position each time collisions are computed, so announcements can\n // read it without waiting for the lagging `dropIndicatorPosition` state to commit.\n const collisionDetection = useCallback<CollisionDetection>(\n (collisionArgs) => {\n const collisions = rawCollisionDetection(collisionArgs);\n const fresh = (collisions?.[0]?.data as DnDKitTree.DragEventData | undefined)?.dropIndicatorPosition;\n if (fresh !== undefined) latestDropIndicatorPositionRef.current = fresh;\n return collisions;\n },\n [rawCollisionDetection],\n );\n\n const dndContextProps = useMemo(\n () => ({\n accessibility: {\n announcements,\n container: document.body,\n },\n modifiers,\n sensors,\n measuring,\n collisionDetection,\n ...dragActionHandlers,\n }),\n [announcements, modifiers, sensors, collisionDetection, dragActionHandlers],\n );\n\n const sortableContextProps = useMemo(\n () =>\n Object.keys(sortedIds).reduce(\n (acc, curr) => {\n acc[curr] = {\n items: sortedIds[curr],\n strategy: () => null,\n };\n return acc;\n },\n {} as Record<string, DnDKitTree.SortableContextPropsType>,\n ),\n [sortedIds],\n );\n\n const activeItem = useMemo(() => {\n if (!active) return null;\n return flattenedItemsDictionary[active.id];\n }, [active, flattenedItemsDictionary]);\n\n const overItem = useMemo(() => {\n if (!over) return null;\n return flattenedItemsDictionary[over.id];\n }, [flattenedItemsDictionary, over]);\n\n return {\n dndContextProps,\n sortableContextProps,\n isDropValid,\n active,\n activeItem,\n over,\n overItem,\n dropIndicatorPosition,\n containerNodesRef,\n };\n};\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -1,14 +1,21 @@
1
1
  import * as React from "react";
2
2
  import { useCallback, useRef } from "react";
3
3
  import { DropIndicatorPosition } from "./constants.js";
4
- import { getDropLandingContext, resolveAccessibleName } from "./utilities.js";
4
+ import { getDropLandingContext, getProjection, resolveAccessibleName } from "./utilities.js";
5
5
  const useTreeAnnouncements = ({
6
6
  flattenedItems,
7
+ visibleFlattenedItems,
7
8
  flattenedItemsDictionary,
8
9
  containersRef,
9
10
  dropIndicatorPosition,
11
+ latestDropIndicatorPositionRef,
12
+ isExpandable,
10
13
  containerNodesRef
11
14
  }) => {
15
+ const getActiveDropIndicatorPosition = useCallback(
16
+ () => latestDropIndicatorPositionRef?.current ?? dropIndicatorPosition,
17
+ [latestDropIndicatorPositionRef, dropIndicatorPosition]
18
+ );
12
19
  const announcedContainerRef = useRef(null);
13
20
  const hasMovedRef = useRef(false);
14
21
  const isStillAtOrigin = useCallback(
@@ -21,19 +28,35 @@ const useTreeAnnouncements = ({
21
28
  );
22
29
  const getMoveContext = useCallback(
23
30
  (active, over) => {
24
- const { isOverContainerOfContainer, overContainer, activeContainer, overIndex } = getDropLandingContext(
31
+ const activeDropIndicatorPosition = getActiveDropIndicatorPosition();
32
+ const { isOverContainerOfContainer, overContainer, activeContainer } = getDropLandingContext(
25
33
  active,
26
34
  over,
27
- dropIndicatorPosition,
35
+ activeDropIndicatorPosition,
28
36
  flattenedItems,
29
37
  flattenedItemsDictionary,
30
38
  containersRef
31
39
  );
32
- const rawPosition = isOverContainerOfContainer ? (flattenedItems[overContainer]?.length ?? 0) + 1 : overIndex + 1;
40
+ const projection = getProjection(
41
+ visibleFlattenedItems,
42
+ flattenedItemsDictionary,
43
+ over,
44
+ activeDropIndicatorPosition,
45
+ isExpandable,
46
+ active
47
+ );
48
+ const rawPosition = isOverContainerOfContainer || projection?.position === void 0 ? (flattenedItems[overContainer]?.length ?? 0) + 1 : projection.position + 1;
33
49
  if (announcedContainerRef.current === null) announcedContainerRef.current = String(activeContainer);
34
50
  return { position: Math.max(1, rawPosition), overContainer: String(overContainer) };
35
51
  },
36
- [dropIndicatorPosition, flattenedItems, flattenedItemsDictionary, containersRef]
52
+ [
53
+ getActiveDropIndicatorPosition,
54
+ flattenedItems,
55
+ visibleFlattenedItems,
56
+ isExpandable,
57
+ flattenedItemsDictionary,
58
+ containersRef
59
+ ]
37
60
  );
38
61
  const onDragStart = useCallback(
39
62
  ({ active: { id } }) => {
@@ -47,7 +70,7 @@ const useTreeAnnouncements = ({
47
70
  ({ active, over }) => {
48
71
  if (!over) return `Draggable item is no longer over a droppable area.`;
49
72
  if (over.id === active.id) return "";
50
- if (dropIndicatorPosition === DropIndicatorPosition.Inside && flattenedItemsDictionary[over.id]) {
73
+ if (getActiveDropIndicatorPosition() === DropIndicatorPosition.Inside && flattenedItemsDictionary[over.id]) {
51
74
  return `Draggable item was moved inside the item at position ${flattenedItemsDictionary[over.id].realIndex + 1}.`;
52
75
  }
53
76
  const { position, overContainer } = getMoveContext(active, over);
@@ -55,13 +78,13 @@ const useTreeAnnouncements = ({
55
78
  hasMovedRef.current = true;
56
79
  return `Draggable item was moved to position ${position}.`;
57
80
  },
58
- [dropIndicatorPosition, flattenedItemsDictionary, getMoveContext, isStillAtOrigin]
81
+ [getActiveDropIndicatorPosition, flattenedItemsDictionary, getMoveContext, isStillAtOrigin]
59
82
  );
60
83
  const onDragOver = useCallback(
61
84
  ({ active, over }) => {
62
85
  if (!over) return `Draggable item is no longer over a droppable area.`;
63
86
  if (over.id === active.id) return "";
64
- if (dropIndicatorPosition === DropIndicatorPosition.Inside && flattenedItemsDictionary[over.id]) {
87
+ if (getActiveDropIndicatorPosition() === DropIndicatorPosition.Inside && flattenedItemsDictionary[over.id]) {
65
88
  return `Draggable item was moved inside the item at position ${flattenedItemsDictionary[over.id].realIndex + 1}.`;
66
89
  }
67
90
  const { position, overContainer } = getMoveContext(active, over);
@@ -75,14 +98,14 @@ const useTreeAnnouncements = ({
75
98
  }
76
99
  return `Draggable item was moved to position ${position}.`;
77
100
  },
78
- [dropIndicatorPosition, flattenedItemsDictionary, getMoveContext, containerNodesRef, isStillAtOrigin]
101
+ [getActiveDropIndicatorPosition, flattenedItemsDictionary, getMoveContext, containerNodesRef, isStillAtOrigin]
79
102
  );
80
103
  const onDragEnd = useCallback(
81
104
  ({ active, over }) => {
82
105
  announcedContainerRef.current = null;
83
106
  hasMovedRef.current = false;
84
107
  if (!over || over.id === active.id) return `Draggable item was dropped at it's original position.`;
85
- if (dropIndicatorPosition === DropIndicatorPosition.Inside && flattenedItemsDictionary[over.id]) {
108
+ if (getActiveDropIndicatorPosition() === DropIndicatorPosition.Inside && flattenedItemsDictionary[over.id]) {
86
109
  return `Draggable item was dropped inside the item at position ${flattenedItemsDictionary[over.id].realIndex + 1}.`;
87
110
  }
88
111
  const { position, overContainer } = getMoveContext(active, over);
@@ -93,7 +116,7 @@ const useTreeAnnouncements = ({
93
116
  }
94
117
  return `Draggable item was dropped at position ${position}.`;
95
118
  },
96
- [dropIndicatorPosition, flattenedItemsDictionary, getMoveContext, containerNodesRef]
119
+ [getActiveDropIndicatorPosition, flattenedItemsDictionary, getMoveContext, containerNodesRef]
97
120
  );
98
121
  const onDragCancel = useCallback(
99
122
  ({ active: { id } }) => {
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../../scripts/build/transpile/react-shim.js", "../../../src/tree/useTreeAnnouncements.tsx"],
4
- "sourcesContent": ["import * as React from 'react';\nexport { React };\n", "import type { Announcements, UniqueIdentifier, Active, Over } from '@dnd-kit/core';\nimport { useCallback, useRef } from 'react';\nimport { DropIndicatorPosition } from './constants.js';\nimport type { DnDKitTree } from './types.js';\nimport { getDropLandingContext, resolveAccessibleName } from './utilities.js';\n\n/**\n * Screen-reader announcements for the tree DnD engine (shared by single- and multi-tree configs).\n *\n * Position: the announced position is the real 1-based landing slot, derived from the same\n * `getDropLandingContext` the reorder (`onDragEnd`) uses \u2014 so the spoken number matches where the item\n * lands, and never underflows to 0.\n *\n * Destination container (name-on-entry): the destination container is named only when the drag *enters*\n * a different container, not on every move within it. dnd-kit fires `onDragOver` exactly when the over\n * target changes (a container crossing is such a change) and `onDragMove` on every coordinate move; so\n * `onDragOver` owns the entry naming (compared against `announcedContainerRef`) while `onDragMove` stays\n * position-only. The name is resolved from the container's accessible name (`aria-label` /\n * `aria-labelledby`), with a generic fallback when unlabeled.\n *\n * Pickup vs move: on pickup, dnd-kit fires `onDragStart` (\"Picked up \u2026 position N\") and then a settle\n * `onDragOver`/`onDragMove`. For a non-top item that settle resolves `over` to a neighbour (not the item\n * itself), which would emit a spurious \"moved to position N\" that overwrites the pickup message. To\n * prevent that, move announcements are suppressed until the item has *genuinely* moved \u2014 i.e. its landing\n * position or container differs from its origin (`hasMovedRef` latch). The latch also lets a later return\n * to the origin position be announced normally.\n */\nexport type UseTreeAnnouncementsArgs<T> = {\n flattenedItems: Record<string, DnDKitTree.Item<T>[]>;\n flattenedItemsDictionary: Record<UniqueIdentifier, DnDKitTree.Item<T>>;\n containersRef: { current: UniqueIdentifier[] };\n dropIndicatorPosition: DnDKitTree.DropIndicatorPositionValues;\n containerNodesRef: { current: Record<string, HTMLElement | null> };\n};\n\nexport const useTreeAnnouncements = <T,>({\n flattenedItems,\n flattenedItemsDictionary,\n containersRef,\n dropIndicatorPosition,\n containerNodesRef,\n}: UseTreeAnnouncementsArgs<T>): Announcements => {\n // The container the user was last told about (the drag origin, then each container they enter).\n // null = not yet initialised for this drag; lazily seeded to the active item's origin container.\n const announcedContainerRef = useRef<string | null>(null);\n // Latches true once the item has genuinely moved from its origin \u2014 suppresses the pickup-settle\n // \"moved\" announcement that would otherwise overwrite the \"Picked up \u2026\" message.\n const hasMovedRef = useRef(false);\n\n // Whether the current landing target is still the item's origin slot (same position AND container).\n // active.realIndex/container reflect the origin throughout the drag (data only mutates on drop).\n const isStillAtOrigin = useCallback(\n (active: Active, position: number, overContainer: string) => {\n const originPosition = (flattenedItemsDictionary[active.id]?.realIndex ?? 0) + 1;\n const originContainer = String(flattenedItemsDictionary[active.id]?.container ?? 'root');\n return position === originPosition && overContainer === originContainer;\n },\n [flattenedItemsDictionary],\n );\n\n const getMoveContext = useCallback(\n (active: Active, over: Over) => {\n const { isOverContainerOfContainer, overContainer, activeContainer, overIndex } = getDropLandingContext(\n active,\n over,\n dropIndicatorPosition,\n flattenedItems,\n flattenedItemsDictionary,\n containersRef,\n );\n const rawPosition = isOverContainerOfContainer ? (flattenedItems[overContainer]?.length ?? 0) + 1 : overIndex + 1;\n if (announcedContainerRef.current === null) announcedContainerRef.current = String(activeContainer);\n return { position: Math.max(1, rawPosition), overContainer: String(overContainer) };\n },\n [dropIndicatorPosition, flattenedItems, flattenedItemsDictionary, containersRef],\n );\n\n const onDragStart: Announcements['onDragStart'] = useCallback(\n ({ active: { id } }) => {\n announcedContainerRef.current = String(flattenedItemsDictionary[id]?.container ?? 'root');\n hasMovedRef.current = false;\n return `Picked up draggable item from position ${(flattenedItemsDictionary[id]?.realIndex ?? 0) + 1}.`;\n },\n [flattenedItemsDictionary],\n );\n\n // onDragMove: position-only, never names the container (fires on every coordinate move).\n // Typed via the non-optional onDragOver signature (same event shape) to keep params strongly typed.\n const onDragMove: Announcements['onDragOver'] = useCallback(\n ({ active, over }) => {\n if (!over) return `Draggable item is no longer over a droppable area.`;\n if (over.id === active.id) return ''; // over itself (e.g. the pickup frame) \u2014 no real move\n if (dropIndicatorPosition === DropIndicatorPosition.Inside && flattenedItemsDictionary[over.id]) {\n return `Draggable item was moved inside the item at position ${flattenedItemsDictionary[over.id].realIndex + 1}.`;\n }\n const { position, overContainer } = getMoveContext(active, over);\n // Suppress the pickup-settle \"moved\" until the item genuinely leaves its origin slot.\n if (!hasMovedRef.current && isStillAtOrigin(active, position, overContainer)) return '';\n hasMovedRef.current = true;\n return `Draggable item was moved to position ${position}.`;\n },\n [dropIndicatorPosition, flattenedItemsDictionary, getMoveContext, isStillAtOrigin],\n );\n\n // onDragOver: fires when the over target changes; names the destination container on entry.\n const onDragOver: Announcements['onDragOver'] = useCallback(\n ({ active, over }) => {\n if (!over) return `Draggable item is no longer over a droppable area.`;\n if (over.id === active.id) return '';\n if (dropIndicatorPosition === DropIndicatorPosition.Inside && flattenedItemsDictionary[over.id]) {\n return `Draggable item was moved inside the item at position ${flattenedItemsDictionary[over.id].realIndex + 1}.`;\n }\n const { position, overContainer } = getMoveContext(active, over);\n // Suppress the pickup-settle \"moved\" until the item genuinely leaves its origin slot.\n if (!hasMovedRef.current && isStillAtOrigin(active, position, overContainer)) return '';\n hasMovedRef.current = true;\n const isEntry = overContainer !== announcedContainerRef.current;\n announcedContainerRef.current = overContainer;\n if (isEntry) {\n const destName = resolveAccessibleName(containerNodesRef.current[overContainer]);\n return `Draggable item was moved to ${destName ?? 'another container'}, position ${position}.`;\n }\n return `Draggable item was moved to position ${position}.`;\n },\n [dropIndicatorPosition, flattenedItemsDictionary, getMoveContext, containerNodesRef, isStillAtOrigin],\n );\n\n const onDragEnd: Announcements['onDragEnd'] = useCallback(\n ({ active, over }) => {\n announcedContainerRef.current = null; // reset for the next drag\n hasMovedRef.current = false;\n if (!over || over.id === active.id) return `Draggable item was dropped at it's original position.`;\n if (dropIndicatorPosition === DropIndicatorPosition.Inside && flattenedItemsDictionary[over.id]) {\n return `Draggable item was dropped inside the item at position ${\n flattenedItemsDictionary[over.id].realIndex + 1\n }.`;\n }\n const { position, overContainer } = getMoveContext(active, over);\n const activeContainer = String(flattenedItemsDictionary[active.id]?.container ?? 'root');\n // Name the destination only when the item is dropped into a different container than it started in.\n if (overContainer !== activeContainer) {\n const destName = resolveAccessibleName(containerNodesRef.current[overContainer]);\n return `Draggable item was dropped in ${destName ?? 'another container'}, position ${position}.`;\n }\n return `Draggable item was dropped at position ${position}.`;\n },\n [dropIndicatorPosition, flattenedItemsDictionary, getMoveContext, containerNodesRef],\n );\n\n const onDragCancel: Announcements['onDragCancel'] = useCallback(\n ({ active: { id } }) => {\n announcedContainerRef.current = null;\n hasMovedRef.current = false;\n return `Dragging was cancelled. Draggable item from position ${\n (flattenedItemsDictionary[id]?.realIndex ?? 0) + 1\n } was dropped at it's initial position.`;\n },\n [flattenedItemsDictionary],\n );\n\n return { onDragStart, onDragOver, onDragMove, onDragEnd, onDragCancel };\n};\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import * as React from 'react';\nexport { React };\n", "import type { Announcements, UniqueIdentifier, Active, Over } from '@dnd-kit/core';\nimport { useCallback, useRef } from 'react';\nimport { DropIndicatorPosition } from './constants.js';\nimport type { DnDKitTree } from './types.js';\nimport { getDropLandingContext, getProjection, resolveAccessibleName } from './utilities.js';\n\n/**\n * Screen-reader announcements for the tree DnD engine (shared by single- and multi-tree configs).\n *\n * Position: the announced position is the 1-based slot shown by the visible drop indicator, derived from\n * the same `getProjection` that positions that indicator (and feeds the consumer's `onReorder`). Sourcing\n * both from one projection makes screen-reader/sighted parity structural \u2014 the spoken number cannot drift\n * from the indicator, including across containers \u2014 and it never underflows to 0. Container-of-container\n * targets (dropping onto an empty/whole container) have no projected slot and append to the end.\n *\n * Destination container (name-on-entry): the destination container is named only when the drag *enters*\n * a different container, not on every move within it. dnd-kit fires `onDragOver` exactly when the over\n * target changes (a container crossing is such a change) and `onDragMove` on every coordinate move; so\n * `onDragOver` owns the entry naming (compared against `announcedContainerRef`) while `onDragMove` stays\n * position-only. The name is resolved from the container's accessible name (`aria-label` /\n * `aria-labelledby`), with a generic fallback when unlabeled.\n *\n * Pickup vs move: on pickup, dnd-kit fires `onDragStart` (\"Picked up \u2026 position N\") and then a settle\n * `onDragOver`/`onDragMove`. For a non-top item that settle resolves `over` to a neighbour (not the item\n * itself), which would emit a spurious \"moved to position N\" that overwrites the pickup message. To\n * prevent that, move announcements are suppressed until the item has *genuinely* moved \u2014 i.e. its landing\n * position or container differs from its origin (`hasMovedRef` latch). The latch also lets a later return\n * to the origin position be announced normally.\n */\nexport type UseTreeAnnouncementsArgs<T> = {\n flattenedItems: Record<string, DnDKitTree.Item<T>[]>;\n // Items with the active item's descendants removed \u2014 the exact list `getProjection` projects against\n // to position the drop indicator. Announcements read the same list so the spoken slot mirrors it.\n visibleFlattenedItems: Record<string, DnDKitTree.Item<T>[]>;\n flattenedItemsDictionary: Record<UniqueIdentifier, DnDKitTree.Item<T>>;\n containersRef: { current: UniqueIdentifier[] };\n dropIndicatorPosition: DnDKitTree.DropIndicatorPositionValues;\n // Fresh drop-indicator position from the latest collision computation. Optional: when absent (e.g. unit\n // tests that drive the handlers synchronously), the `dropIndicatorPosition` prop is the source of truth.\n // When present, it is preferred because the prop lags by one render during a live drag (see the config).\n latestDropIndicatorPositionRef?: { current: DnDKitTree.DropIndicatorPositionValues };\n isExpandable: boolean;\n containerNodesRef: { current: Record<string, HTMLElement | null> };\n};\n\nexport const useTreeAnnouncements = <T,>({\n flattenedItems,\n visibleFlattenedItems,\n flattenedItemsDictionary,\n containersRef,\n dropIndicatorPosition,\n latestDropIndicatorPositionRef,\n isExpandable,\n containerNodesRef,\n}: UseTreeAnnouncementsArgs<T>): Announcements => {\n // Prefer the fresh collision-time position; fall back to the prop when no ref is provided.\n const getActiveDropIndicatorPosition = useCallback(\n () => latestDropIndicatorPositionRef?.current ?? dropIndicatorPosition,\n [latestDropIndicatorPositionRef, dropIndicatorPosition],\n );\n // The container the user was last told about (the drag origin, then each container they enter).\n // null = not yet initialised for this drag; lazily seeded to the active item's origin container.\n const announcedContainerRef = useRef<string | null>(null);\n // Latches true once the item has genuinely moved from its origin \u2014 suppresses the pickup-settle\n // \"moved\" announcement that would otherwise overwrite the \"Picked up \u2026\" message.\n const hasMovedRef = useRef(false);\n\n // Whether the current landing target is still the item's origin slot (same position AND container).\n // active.realIndex/container reflect the origin throughout the drag (data only mutates on drop).\n const isStillAtOrigin = useCallback(\n (active: Active, position: number, overContainer: string) => {\n const originPosition = (flattenedItemsDictionary[active.id]?.realIndex ?? 0) + 1;\n const originContainer = String(flattenedItemsDictionary[active.id]?.container ?? 'root');\n return position === originPosition && overContainer === originContainer;\n },\n [flattenedItemsDictionary],\n );\n\n const getMoveContext = useCallback(\n (active: Active, over: Over) => {\n const activeDropIndicatorPosition = getActiveDropIndicatorPosition();\n // Destination container (for entry detection + accessible-name resolution) stays on the landing-context\n // path; only the numeric position is sourced from the projection so it mirrors the visible indicator.\n const { isOverContainerOfContainer, overContainer, activeContainer } = getDropLandingContext(\n active,\n over,\n activeDropIndicatorPosition,\n flattenedItems,\n flattenedItemsDictionary,\n containersRef,\n );\n const projection = getProjection(\n visibleFlattenedItems,\n flattenedItemsDictionary,\n over,\n activeDropIndicatorPosition,\n isExpandable,\n active,\n );\n // Container-of-container targets have no projected slot \u2014 append to the container's end (as before).\n const rawPosition =\n isOverContainerOfContainer || projection?.position === undefined\n ? (flattenedItems[overContainer]?.length ?? 0) + 1\n : projection.position + 1;\n if (announcedContainerRef.current === null) announcedContainerRef.current = String(activeContainer);\n return { position: Math.max(1, rawPosition), overContainer: String(overContainer) };\n },\n [\n getActiveDropIndicatorPosition,\n flattenedItems,\n visibleFlattenedItems,\n isExpandable,\n flattenedItemsDictionary,\n containersRef,\n ],\n );\n\n const onDragStart: Announcements['onDragStart'] = useCallback(\n ({ active: { id } }) => {\n announcedContainerRef.current = String(flattenedItemsDictionary[id]?.container ?? 'root');\n hasMovedRef.current = false;\n return `Picked up draggable item from position ${(flattenedItemsDictionary[id]?.realIndex ?? 0) + 1}.`;\n },\n [flattenedItemsDictionary],\n );\n\n // onDragMove: position-only, never names the container (fires on every coordinate move).\n // Typed via the non-optional onDragOver signature (same event shape) to keep params strongly typed.\n const onDragMove: Announcements['onDragOver'] = useCallback(\n ({ active, over }) => {\n if (!over) return `Draggable item is no longer over a droppable area.`;\n if (over.id === active.id) return ''; // over itself (e.g. the pickup frame) \u2014 no real move\n if (getActiveDropIndicatorPosition() === DropIndicatorPosition.Inside && flattenedItemsDictionary[over.id]) {\n return `Draggable item was moved inside the item at position ${flattenedItemsDictionary[over.id].realIndex + 1}.`;\n }\n const { position, overContainer } = getMoveContext(active, over);\n // Suppress the pickup-settle \"moved\" until the item genuinely leaves its origin slot.\n if (!hasMovedRef.current && isStillAtOrigin(active, position, overContainer)) return '';\n hasMovedRef.current = true;\n return `Draggable item was moved to position ${position}.`;\n },\n [getActiveDropIndicatorPosition, flattenedItemsDictionary, getMoveContext, isStillAtOrigin],\n );\n\n // onDragOver: fires when the over target changes; names the destination container on entry.\n const onDragOver: Announcements['onDragOver'] = useCallback(\n ({ active, over }) => {\n if (!over) return `Draggable item is no longer over a droppable area.`;\n if (over.id === active.id) return '';\n if (getActiveDropIndicatorPosition() === DropIndicatorPosition.Inside && flattenedItemsDictionary[over.id]) {\n return `Draggable item was moved inside the item at position ${flattenedItemsDictionary[over.id].realIndex + 1}.`;\n }\n const { position, overContainer } = getMoveContext(active, over);\n // Suppress the pickup-settle \"moved\" until the item genuinely leaves its origin slot.\n if (!hasMovedRef.current && isStillAtOrigin(active, position, overContainer)) return '';\n hasMovedRef.current = true;\n const isEntry = overContainer !== announcedContainerRef.current;\n announcedContainerRef.current = overContainer;\n if (isEntry) {\n const destName = resolveAccessibleName(containerNodesRef.current[overContainer]);\n return `Draggable item was moved to ${destName ?? 'another container'}, position ${position}.`;\n }\n return `Draggable item was moved to position ${position}.`;\n },\n [getActiveDropIndicatorPosition, flattenedItemsDictionary, getMoveContext, containerNodesRef, isStillAtOrigin],\n );\n\n const onDragEnd: Announcements['onDragEnd'] = useCallback(\n ({ active, over }) => {\n announcedContainerRef.current = null; // reset for the next drag\n hasMovedRef.current = false;\n if (!over || over.id === active.id) return `Draggable item was dropped at it's original position.`;\n if (getActiveDropIndicatorPosition() === DropIndicatorPosition.Inside && flattenedItemsDictionary[over.id]) {\n return `Draggable item was dropped inside the item at position ${\n flattenedItemsDictionary[over.id].realIndex + 1\n }.`;\n }\n const { position, overContainer } = getMoveContext(active, over);\n const activeContainer = String(flattenedItemsDictionary[active.id]?.container ?? 'root');\n // Name the destination only when the item is dropped into a different container than it started in.\n if (overContainer !== activeContainer) {\n const destName = resolveAccessibleName(containerNodesRef.current[overContainer]);\n return `Draggable item was dropped in ${destName ?? 'another container'}, position ${position}.`;\n }\n return `Draggable item was dropped at position ${position}.`;\n },\n [getActiveDropIndicatorPosition, flattenedItemsDictionary, getMoveContext, containerNodesRef],\n );\n\n const onDragCancel: Announcements['onDragCancel'] = useCallback(\n ({ active: { id } }) => {\n announcedContainerRef.current = null;\n hasMovedRef.current = false;\n return `Dragging was cancelled. Draggable item from position ${\n (flattenedItemsDictionary[id]?.realIndex ?? 0) + 1\n } was dropped at it's initial position.`;\n },\n [flattenedItemsDictionary],\n );\n\n return { onDragStart, onDragOver, onDragMove, onDragEnd, onDragCancel };\n};\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -3,9 +3,11 @@ import type { DnDKitTree } from './types.js';
3
3
  /**
4
4
  * Screen-reader announcements for the tree DnD engine (shared by single- and multi-tree configs).
5
5
  *
6
- * Position: the announced position is the real 1-based landing slot, derived from the same
7
- * `getDropLandingContext` the reorder (`onDragEnd`) uses so the spoken number matches where the item
8
- * lands, and never underflows to 0.
6
+ * Position: the announced position is the 1-based slot shown by the visible drop indicator, derived from
7
+ * the same `getProjection` that positions that indicator (and feeds the consumer's `onReorder`). Sourcing
8
+ * both from one projection makes screen-reader/sighted parity structural — the spoken number cannot drift
9
+ * from the indicator, including across containers — and it never underflows to 0. Container-of-container
10
+ * targets (dropping onto an empty/whole container) have no projected slot and append to the end.
9
11
  *
10
12
  * Destination container (name-on-entry): the destination container is named only when the drag *enters*
11
13
  * a different container, not on every move within it. dnd-kit fires `onDragOver` exactly when the over
@@ -23,13 +25,18 @@ import type { DnDKitTree } from './types.js';
23
25
  */
24
26
  export type UseTreeAnnouncementsArgs<T> = {
25
27
  flattenedItems: Record<string, DnDKitTree.Item<T>[]>;
28
+ visibleFlattenedItems: Record<string, DnDKitTree.Item<T>[]>;
26
29
  flattenedItemsDictionary: Record<UniqueIdentifier, DnDKitTree.Item<T>>;
27
30
  containersRef: {
28
31
  current: UniqueIdentifier[];
29
32
  };
30
33
  dropIndicatorPosition: DnDKitTree.DropIndicatorPositionValues;
34
+ latestDropIndicatorPositionRef?: {
35
+ current: DnDKitTree.DropIndicatorPositionValues;
36
+ };
37
+ isExpandable: boolean;
31
38
  containerNodesRef: {
32
39
  current: Record<string, HTMLElement | null>;
33
40
  };
34
41
  };
35
- export declare const useTreeAnnouncements: <T>({ flattenedItems, flattenedItemsDictionary, containersRef, dropIndicatorPosition, containerNodesRef, }: UseTreeAnnouncementsArgs<T>) => Announcements;
42
+ export declare const useTreeAnnouncements: <T>({ flattenedItems, visibleFlattenedItems, flattenedItemsDictionary, containersRef, dropIndicatorPosition, latestDropIndicatorPositionRef, isExpandable, containerNodesRef, }: UseTreeAnnouncementsArgs<T>) => Announcements;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@elliemae/ds-drag-and-drop",
3
- "version": "3.70.0-next.49",
3
+ "version": "3.70.0-next.50",
4
4
  "license": "MIT",
5
5
  "description": "ICE MT - Dimsum - Drag And Drop",
6
6
  "files": [
@@ -39,15 +39,15 @@
39
39
  "@dnd-kit/core": "~6.0.8",
40
40
  "@dnd-kit/modifiers": "~6.0.1",
41
41
  "@dnd-kit/sortable": "~7.0.2",
42
- "@elliemae/ds-fast-list": "3.70.0-next.49",
43
- "@elliemae/ds-props-helpers": "3.70.0-next.49",
44
- "@elliemae/ds-system": "3.70.0-next.49",
45
- "@elliemae/ds-typescript-helpers": "3.70.0-next.49"
42
+ "@elliemae/ds-props-helpers": "3.70.0-next.50",
43
+ "@elliemae/ds-fast-list": "3.70.0-next.50",
44
+ "@elliemae/ds-system": "3.70.0-next.50",
45
+ "@elliemae/ds-typescript-helpers": "3.70.0-next.50"
46
46
  },
47
47
  "devDependencies": {
48
48
  "jest": "^30.0.0",
49
49
  "styled-components": "~5.3.9",
50
- "@elliemae/ds-monorepo-devops": "3.70.0-next.49"
50
+ "@elliemae/ds-monorepo-devops": "3.70.0-next.50"
51
51
  },
52
52
  "peerDependencies": {
53
53
  "lodash-es": "^4.18.1",