UNPKG

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