UNPKG

@awsui/components-react

Version:

On July 19th, 2022, we launched [Cloudscape Design System](https://cloudscape.design). Cloudscape is an evolution of AWS-UI. It consists of user interface guidelines, front-end components, design resources, and development tools for building intuitive, en

144 lines 6.38 kB
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 import { useRef, useState } from 'react'; import { closestCenter, PointerSensor, useSensor, useSensors, } from '@dnd-kit/core'; import { hasSortableData } from '@dnd-kit/sortable'; import { KeyboardSensor } from './keyboard-sensor'; var KeyboardCode; (function (KeyboardCode) { KeyboardCode["Space"] = "Space"; KeyboardCode["Down"] = "ArrowDown"; KeyboardCode["Right"] = "ArrowRight"; KeyboardCode["Left"] = "ArrowLeft"; KeyboardCode["Up"] = "ArrowUp"; KeyboardCode["Esc"] = "Escape"; KeyboardCode["Enter"] = "Enter"; })(KeyboardCode || (KeyboardCode = {})); // A custom collision detection algorithm is used when using a keyboard to // work around an unexpected behavior when reordering items of variable height // with the keyboard. // Neither closestCenter nor closestCorners work really well for this case, // because the center (or corners) of a tall rectangle might be so low that it // is detected as being closest to the rectangle below of the one it should // actually swap with. // Instead of relying on coordinates, the expected results are achieved by // moving X positions up or down in the initially sorted array, depending on // the desired direction. // We let our collisionDetection and customCoordinateGetter use the same // getClosestId function which takes its value from the current component // state, to make sure they are always in sync. export default function useDragAndDropReorder({ items, itemDefinition, }) { const isKeyboard = useRef(false); const positionDelta = useRef(0); const [activeItemId, setActiveItemId] = useState(null); const setActiveItem = (id) => { setActiveItemId(id); if (!id) { isKeyboard.current = false; positionDelta.current = 0; } }; const handleKeyDown = (event) => { if (isKeyboard.current && activeItemId) { const currentTargetIndex = items.findIndex(item => itemDefinition.id(item) === activeItemId) + positionDelta.current; if (event.key === 'ArrowDown' && currentTargetIndex < items.length - 1) { positionDelta.current += 1; } else if (event.key === 'ArrowUp' && currentTargetIndex > 0) { positionDelta.current -= 1; } } if (activeItemId && isEscape(event.key)) { // Prevent modal from closing when pressing Esc to cancel the dragging action event.stopPropagation(); } }; const getClosestId = (active) => { if (positionDelta.current === 0) { return active.id; } const currentIndex = items.findIndex(item => itemDefinition.id(item) === active.id); const newIndex = Math.max(0, Math.min(items.length - 1, currentIndex + positionDelta.current)); return itemDefinition.id(items[newIndex]); }; const collisionDetection = ({ active, collisionRect, droppableContainers, droppableRects, pointerCoordinates, }) => { if (isKeyboard.current) { // For keyboard interaction, determine the colliding container based on the movements made by the arrow keys, // via getClosestId const collidingContainer = getCollidingContainer({ activeId: active.id, closestId: getClosestId(active), droppableContainers, }); return collidingContainer ? [collidingContainer] : []; } else { // For mouse interaction, use the closest center algorithm return closestCenter({ active, collisionRect, droppableRects, droppableContainers, pointerCoordinates }); } }; const coordinateGetter = (event, { context: { active, collisionRect, droppableRects, droppableContainers } }) => { if (event.code === KeyboardCode.Up || event.code === KeyboardCode.Down) { event.preventDefault(); if (!active || !collisionRect) { return; } const closestId = getClosestId(active); if (closestId !== null) { const activeDroppable = droppableContainers.get(active.id); const newDroppable = droppableContainers.get(closestId); const newRect = newDroppable ? droppableRects.get(newDroppable.id) : null; const newNode = newDroppable === null || newDroppable === void 0 ? void 0 : newDroppable.node.current; if (newNode && newRect && activeDroppable && newDroppable) { const isAfterActive = isAfter(activeDroppable, newDroppable); const offset = { x: isAfterActive ? collisionRect.width - newRect.width : 0, y: isAfterActive ? collisionRect.height - newRect.height : 0, }; const rectCoordinates = { x: newRect.left, y: newRect.top, }; return { x: rectCoordinates.x - offset.x, y: rectCoordinates.y - offset.y, }; } } } }; const sensors = useSensors(useSensor(PointerSensor), useSensor(KeyboardSensor, { coordinateGetter, onActivation: () => { isKeyboard.current = true; }, })); return { activeItemId, setActiveItemId: setActiveItem, collisionDetection, coordinateGetter, handleKeyDown, sensors, }; } function isAfter(a, b) { return hasSortableData(a) && hasSortableData(b) && a.data.current.sortable.index < b.data.current.sortable.index; } function getCollidingContainer({ activeId, closestId, droppableContainers, }) { if (closestId === activeId) { return; } const collidingContainer = droppableContainers.find(({ id }) => id === closestId); if (collidingContainer) { return { id: collidingContainer.id, data: { droppableContainer: collidingContainer, value: 0, }, }; } } const isEscape = (key) => key === 'Escape' || key === 'Esc'; //# sourceMappingURL=use-drag-and-drop-reorder.js.map