| 105 | * based drag and drop, in addition to full parity for keyboard and screen reader users. |
| 106 | */ |
| 107 | export function useDrag(options: DragOptions): DragResult { |
| 108 | let {hasDragButton, isDisabled} = options; |
| 109 | let stringFormatter = useLocalizedStringFormatter(intlMessages, '@react-aria/dnd'); |
| 110 | let state = useRef({ |
| 111 | options, |
| 112 | x: 0, |
| 113 | y: 0 |
| 114 | }).current; |
| 115 | state.options = options; |
| 116 | let isDraggingRef = useRef<Element | null>(null); |
| 117 | let [isDragging, setDraggingState] = useState(false); |
| 118 | let setDragging = (element: Element | null) => { |
| 119 | isDraggingRef.current = element; |
| 120 | setDraggingState(!!element); |
| 121 | }; |
| 122 | let {addGlobalListener, removeAllGlobalListeners} = useGlobalListeners(); |
| 123 | let modalityOnPointerDown = useRef<string>(null); |
| 124 | |
| 125 | let onDragStart = (e: DragEvent) => { |
| 126 | if (e.defaultPrevented) { |
| 127 | return; |
| 128 | } |
| 129 | |
| 130 | // Prevent the drag event from propagating to any parent draggables |
| 131 | e.stopPropagation(); |
| 132 | |
| 133 | // If this drag was initiated by a mobile screen reader (e.g. VoiceOver or TalkBack), enter virtual dragging mode. |
| 134 | if (modalityOnPointerDown.current === 'virtual') { |
| 135 | e.preventDefault(); |
| 136 | startDragging(getEventTarget(e) as HTMLElement); |
| 137 | modalityOnPointerDown.current = null; |
| 138 | return; |
| 139 | } |
| 140 | |
| 141 | if (typeof options.onDragStart === 'function') { |
| 142 | options.onDragStart({ |
| 143 | type: 'dragstart', |
| 144 | x: e.clientX, |
| 145 | y: e.clientY |
| 146 | }); |
| 147 | } |
| 148 | |
| 149 | let items = options.getItems(); |
| 150 | // Clear existing data (e.g. selected text on the page would be included in some browsers) |
| 151 | e.dataTransfer.clearData?.(); |
| 152 | writeToDataTransfer(e.dataTransfer, items); |
| 153 | |
| 154 | let allowed = DROP_OPERATION.all; |
| 155 | if (typeof options.getAllowedDropOperations === 'function') { |
| 156 | let allowedOperations = options.getAllowedDropOperations(); |
| 157 | allowed = DROP_OPERATION.none; |
| 158 | for (let operation of allowedOperations) { |
| 159 | allowed |= DROP_OPERATION[operation] || DROP_OPERATION.none; |
| 160 | } |
| 161 | } |
| 162 | |
| 163 | setGlobalAllowedDropOperations(allowed); |
| 164 | let effectAllowed = EFFECT_ALLOWED[allowed] || 'none'; |