UNPKG

6.44 kBPlain TextView Raw
1/*
2 * Copyright 2020 Adobe. All rights reserved.
3 * This file is licensed to you under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License. You may obtain a copy
5 * of the License at http://www.apache.org/licenses/LICENSE-2.0
6 *
7 * Unless required by applicable law or agreed to in writing, software distributed under
8 * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
9 * OF ANY KIND, either express or implied. See the License for the specific language
10 * governing permissions and limitations under the License.
11 */
12
13import {getOffset} from './getOffset';
14import {Orientation} from '@react-types/shared';
15import React, {HTMLAttributes, MutableRefObject, useRef} from 'react';
16
17interface UseDrag1DProps {
18 containerRef: MutableRefObject<HTMLElement>,
19 reverse?: boolean,
20 orientation?: Orientation,
21 onHover?: (hovered: boolean) => void,
22 onDrag?: (dragging: boolean) => void,
23 onPositionChange?: (position: number) => void,
24 onIncrement?: () => void,
25 onDecrement?: () => void,
26 onIncrementToMax?: () => void,
27 onDecrementToMin?: () => void,
28 onCollapseToggle?: () => void
29}
30
31// Keep track of elements that we are currently handling dragging for via useDrag1D.
32// If there's an ancestor and a descendant both using useDrag1D(), and the user starts
33// dragging the descendant, we don't want useDrag1D events to fire for the ancestor.
34const draggingElements: HTMLElement[] = [];
35
36// created for splitview, this should be reusable for things like sliders/dials
37// It also handles keyboard events on the target allowing for increment/decrement by a given stepsize as well as minifying/maximizing and toggling between minified and previous size
38// It can also take a 'reverse' param to say if we should measure from the right/bottom instead of the top/left
39// It can also handle either a vertical or horizontal movement, but not both at the same time
40
41export function useDrag1D(props: UseDrag1DProps): HTMLAttributes<HTMLElement> {
42 console.warn('useDrag1D is deprecated, please use `useMove` instead https://react-spectrum.adobe.com/react-aria/useMove.html');
43 let {containerRef, reverse, orientation, onHover, onDrag, onPositionChange, onIncrement, onDecrement, onIncrementToMax, onDecrementToMin, onCollapseToggle} = props;
44 let getPosition = (e) => orientation === 'horizontal' ? e.clientX : e.clientY;
45 let getNextOffset = (e: MouseEvent) => {
46 let containerOffset = getOffset(containerRef.current, reverse, orientation);
47 let mouseOffset = getPosition(e);
48 let nextOffset = reverse ? containerOffset - mouseOffset : mouseOffset - containerOffset;
49 return nextOffset;
50 };
51 let dragging = useRef(false);
52 let prevPosition = useRef(0);
53
54 // Keep track of the current handlers in a ref so that the events can access them.
55 let handlers = useRef({onPositionChange, onDrag});
56 handlers.current.onDrag = onDrag;
57 handlers.current.onPositionChange = onPositionChange;
58
59 let onMouseDragged = (e: MouseEvent) => {
60 e.preventDefault();
61 let nextOffset = getNextOffset(e);
62 if (!dragging.current) {
63 dragging.current = true;
64 if (handlers.current.onDrag) {
65 handlers.current.onDrag(true);
66 }
67 if (handlers.current.onPositionChange) {
68 handlers.current.onPositionChange(nextOffset);
69 }
70 }
71 if (prevPosition.current === nextOffset) {
72 return;
73 }
74 prevPosition.current = nextOffset;
75 if (onPositionChange) {
76 onPositionChange(nextOffset);
77 }
78 };
79
80 let onMouseUp = (e: MouseEvent) => {
81 const target = e.target as HTMLElement;
82 dragging.current = false;
83 let nextOffset = getNextOffset(e);
84 if (handlers.current.onDrag) {
85 handlers.current.onDrag(false);
86 }
87 if (handlers.current.onPositionChange) {
88 handlers.current.onPositionChange(nextOffset);
89 }
90
91 draggingElements.splice(draggingElements.indexOf(target), 1);
92 window.removeEventListener('mouseup', onMouseUp, false);
93 window.removeEventListener('mousemove', onMouseDragged, false);
94 };
95
96 let onMouseDown = (e: React.MouseEvent<HTMLElement>) => {
97 const target = e.currentTarget;
98 // If we're already handling dragging on a descendant with useDrag1D, then
99 // we don't want to handle the drag motion on this target as well.
100 if (draggingElements.some(elt => target.contains(elt))) {
101 return;
102 }
103 draggingElements.push(target);
104 window.addEventListener('mousemove', onMouseDragged, false);
105 window.addEventListener('mouseup', onMouseUp, false);
106 };
107
108 let onMouseEnter = () => {
109 if (onHover) {
110 onHover(true);
111 }
112 };
113
114 let onMouseOut = () => {
115 if (onHover) {
116 onHover(false);
117 }
118 };
119
120 let onKeyDown = (e) => {
121 switch (e.key) {
122 case 'Left':
123 case 'ArrowLeft':
124 if (orientation === 'horizontal') {
125 e.preventDefault();
126 if (onDecrement && !reverse) {
127 onDecrement();
128 } else if (onIncrement && reverse) {
129 onIncrement();
130 }
131 }
132 break;
133 case 'Up':
134 case 'ArrowUp':
135 if (orientation === 'vertical') {
136 e.preventDefault();
137 if (onDecrement && !reverse) {
138 onDecrement();
139 } else if (onIncrement && reverse) {
140 onIncrement();
141 }
142 }
143 break;
144 case 'Right':
145 case 'ArrowRight':
146 if (orientation === 'horizontal') {
147 e.preventDefault();
148 if (onIncrement && !reverse) {
149 onIncrement();
150 } else if (onDecrement && reverse) {
151 onDecrement();
152 }
153 }
154 break;
155 case 'Down':
156 case 'ArrowDown':
157 if (orientation === 'vertical') {
158 e.preventDefault();
159 if (onIncrement && !reverse) {
160 onIncrement();
161 } else if (onDecrement && reverse) {
162 onDecrement();
163 }
164 }
165 break;
166 case 'Home':
167 e.preventDefault();
168 if (onDecrementToMin) {
169 onDecrementToMin();
170 }
171 break;
172 case 'End':
173 e.preventDefault();
174 if (onIncrementToMax) {
175 onIncrementToMax();
176 }
177 break;
178 case 'Enter':
179 e.preventDefault();
180 if (onCollapseToggle) {
181 onCollapseToggle();
182 }
183 break;
184 }
185 };
186
187 return {onMouseDown, onMouseEnter, onMouseOut, onKeyDown};
188}