UNPKG

11.9 kBJavaScriptView Raw
1"use strict";
2/*
3 * Copyright 2016 Palantir Technologies, Inc. All rights reserved.
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17Object.defineProperty(exports, "__esModule", { value: true });
18exports.Handle = void 0;
19var tslib_1 = require("tslib");
20var classnames_1 = tslib_1.__importDefault(require("classnames"));
21var React = tslib_1.__importStar(require("react"));
22var common_1 = require("../../common");
23var props_1 = require("../../common/props");
24var utils_1 = require("../../common/utils");
25var sliderUtils_1 = require("./sliderUtils");
26// props that require number values, for validation
27var NUMBER_PROPS = ["max", "min", "stepSize", "tickSize", "value"];
28/** Internal component for a Handle with click/drag/keyboard logic to determine a new value. */
29var Handle = /** @class */ (function (_super) {
30 tslib_1.__extends(Handle, _super);
31 function Handle() {
32 var _this = _super !== null && _super.apply(this, arguments) || this;
33 _this.state = {
34 isMoving: false,
35 };
36 _this.handleElement = null;
37 _this.refHandlers = {
38 handle: function (el) { return (_this.handleElement = el); },
39 };
40 _this.beginHandleMovement = function (event) {
41 document.addEventListener("mousemove", _this.handleHandleMovement);
42 document.addEventListener("mouseup", _this.endHandleMovement);
43 _this.setState({ isMoving: true });
44 _this.changeValue(_this.clientToValue(_this.mouseEventClientOffset(event)));
45 };
46 _this.beginHandleTouchMovement = function (event) {
47 document.addEventListener("touchmove", _this.handleHandleTouchMovement);
48 document.addEventListener("touchend", _this.endHandleTouchMovement);
49 document.addEventListener("touchcancel", _this.endHandleTouchMovement);
50 _this.setState({ isMoving: true });
51 _this.changeValue(_this.clientToValue(_this.touchEventClientOffset(event)));
52 };
53 _this.getStyleProperties = function () {
54 if (_this.handleElement == null) {
55 return {};
56 }
57 // The handle midpoint of RangeSlider is actually shifted by a margin to
58 // be on the edge of the visible handle element. Because the midpoint
59 // calculation does not take this margin into account, we instead
60 // measure the long side (which is equal to the short side plus the
61 // margin).
62 var _a = _this.props, _b = _a.min, min = _b === void 0 ? 0 : _b, tickSizeRatio = _a.tickSizeRatio, value = _a.value, vertical = _a.vertical;
63 var handleMidpoint = _this.getHandleMidpointAndOffset(_this.handleElement, true).handleMidpoint;
64 var offsetRatio = (value - min) * tickSizeRatio;
65 var offsetCalc = "calc(".concat((0, sliderUtils_1.formatPercentage)(offsetRatio), " - ").concat(handleMidpoint, "px)");
66 return vertical ? { bottom: offsetCalc } : { left: offsetCalc };
67 };
68 _this.endHandleMovement = function (event) {
69 _this.handleMoveEndedAt(_this.mouseEventClientOffset(event));
70 };
71 _this.endHandleTouchMovement = function (event) {
72 _this.handleMoveEndedAt(_this.touchEventClientOffset(event));
73 };
74 _this.handleMoveEndedAt = function (clientPixel) {
75 var _a, _b;
76 _this.removeDocumentEventListeners();
77 _this.setState({ isMoving: false });
78 // always invoke onRelease; changeValue may call onChange if value is different
79 var finalValue = _this.changeValue(_this.clientToValue(clientPixel));
80 (_b = (_a = _this.props).onRelease) === null || _b === void 0 ? void 0 : _b.call(_a, finalValue);
81 };
82 _this.handleHandleMovement = function (event) {
83 _this.handleMovedTo(_this.mouseEventClientOffset(event));
84 };
85 _this.handleHandleTouchMovement = function (event) {
86 _this.handleMovedTo(_this.touchEventClientOffset(event));
87 };
88 _this.handleMovedTo = function (clientPixel) {
89 if (_this.state.isMoving && !_this.props.disabled) {
90 _this.changeValue(_this.clientToValue(clientPixel));
91 }
92 };
93 _this.handleKeyDown = function (event) {
94 var _a = _this.props, stepSize = _a.stepSize, value = _a.value;
95 // HACKHACK: https://github.com/palantir/blueprint/issues/4165
96 /* eslint-disable-next-line deprecation/deprecation */
97 var which = event.which;
98 if (which === common_1.Keys.ARROW_DOWN || which === common_1.Keys.ARROW_LEFT) {
99 _this.changeValue(value - stepSize);
100 // this key event has been handled! prevent browser scroll on up/down
101 event.preventDefault();
102 }
103 else if (which === common_1.Keys.ARROW_UP || which === common_1.Keys.ARROW_RIGHT) {
104 _this.changeValue(value + stepSize);
105 event.preventDefault();
106 }
107 };
108 _this.handleKeyUp = function (event) {
109 var _a, _b;
110 // HACKHACK: https://github.com/palantir/blueprint/issues/4165
111 /* eslint-disable-next-line deprecation/deprecation */
112 if ([common_1.Keys.ARROW_UP, common_1.Keys.ARROW_DOWN, common_1.Keys.ARROW_LEFT, common_1.Keys.ARROW_RIGHT].indexOf(event.which) >= 0) {
113 (_b = (_a = _this.props).onRelease) === null || _b === void 0 ? void 0 : _b.call(_a, _this.props.value);
114 }
115 };
116 return _this;
117 }
118 Handle.prototype.componentDidMount = function () {
119 // The first time this component renders, it has no ref to the handle and thus incorrectly centers the handle.
120 // Therefore, on the first mount, force a re-render to center the handle with the ref'd component.
121 this.forceUpdate();
122 };
123 Handle.prototype.render = function () {
124 var _a;
125 var _b = this.props, className = _b.className, disabled = _b.disabled, label = _b.label, min = _b.min, max = _b.max, value = _b.value, vertical = _b.vertical, htmlProps = _b.htmlProps;
126 var isMoving = this.state.isMoving;
127 return (React.createElement("span", tslib_1.__assign({ role: "slider", tabIndex: 0 }, htmlProps, { className: (0, classnames_1.default)(common_1.Classes.SLIDER_HANDLE, (_a = {}, _a[common_1.Classes.ACTIVE] = isMoving, _a), className), onKeyDown: disabled ? undefined : this.handleKeyDown, onKeyUp: disabled ? undefined : this.handleKeyUp, onMouseDown: disabled ? undefined : this.beginHandleMovement, onTouchStart: disabled ? undefined : this.beginHandleTouchMovement, ref: this.refHandlers.handle, style: this.getStyleProperties(), "aria-valuemin": min, "aria-valuemax": max, "aria-valuenow": value, "aria-orientation": vertical ? "vertical" : "horizontal" }), label == null ? null : React.createElement("span", { className: common_1.Classes.SLIDER_LABEL }, label)));
128 };
129 Handle.prototype.componentWillUnmount = function () {
130 this.removeDocumentEventListeners();
131 };
132 /** Convert client pixel to value between min and max. */
133 Handle.prototype.clientToValue = function (clientPixel) {
134 var _a = this.props, stepSize = _a.stepSize, tickSize = _a.tickSize, value = _a.value, vertical = _a.vertical;
135 if (this.handleElement == null) {
136 return value;
137 }
138 // #1769: this logic doesn't work perfectly when the tick size is
139 // smaller than the handle size; it may be off by a tick or two.
140 var clientPixelNormalized = vertical ? window.innerHeight - clientPixel : clientPixel;
141 var handleCenterPixel = this.getHandleElementCenterPixel(this.handleElement);
142 var pixelDelta = clientPixelNormalized - handleCenterPixel;
143 if (isNaN(pixelDelta)) {
144 return value;
145 }
146 // convert pixels to range value in increments of `stepSize`
147 return value + Math.round(pixelDelta / (tickSize * stepSize)) * stepSize;
148 };
149 Handle.prototype.mouseEventClientOffset = function (event) {
150 return this.props.vertical ? event.clientY : event.clientX;
151 };
152 Handle.prototype.touchEventClientOffset = function (event) {
153 var touch = event.changedTouches[0];
154 return this.props.vertical ? touch.clientY : touch.clientX;
155 };
156 Handle.prototype.validateProps = function (props) {
157 for (var _i = 0, NUMBER_PROPS_1 = NUMBER_PROPS; _i < NUMBER_PROPS_1.length; _i++) {
158 var prop = NUMBER_PROPS_1[_i];
159 if (typeof props[prop] !== "number") {
160 throw new Error("[Blueprint] <Handle> requires number value for ".concat(prop, " prop"));
161 }
162 }
163 };
164 /** Clamp value and invoke callback if it differs from current value */
165 Handle.prototype.changeValue = function (newValue, callback) {
166 if (callback === void 0) { callback = this.props.onChange; }
167 newValue = this.clamp(newValue);
168 if (!isNaN(newValue) && this.props.value !== newValue) {
169 callback === null || callback === void 0 ? void 0 : callback(newValue);
170 }
171 return newValue;
172 };
173 /** Clamp value between min and max props */
174 Handle.prototype.clamp = function (value) {
175 return (0, utils_1.clamp)(value, this.props.min, this.props.max);
176 };
177 Handle.prototype.getHandleElementCenterPixel = function (handleElement) {
178 var _a = this.getHandleMidpointAndOffset(handleElement), handleMidpoint = _a.handleMidpoint, handleOffset = _a.handleOffset;
179 return handleOffset + handleMidpoint;
180 };
181 Handle.prototype.getHandleMidpointAndOffset = function (handleElement, useOppositeDimension) {
182 if (useOppositeDimension === void 0) { useOppositeDimension = false; }
183 if (handleElement == null) {
184 return { handleMidpoint: 0, handleOffset: 0 };
185 }
186 var vertical = this.props.vertical;
187 // getBoundingClientRect().height includes border size; clientHeight does not.
188 var handleRect = handleElement.getBoundingClientRect();
189 var sizeKey = vertical
190 ? useOppositeDimension
191 ? "width"
192 : "height"
193 : useOppositeDimension
194 ? "height"
195 : "width";
196 // "bottom" value seems to be consistently incorrect, so explicitly
197 // calculate it using the window offset instead.
198 var handleOffset = vertical ? window.innerHeight - (handleRect.top + handleRect[sizeKey]) : handleRect.left;
199 return { handleMidpoint: handleRect[sizeKey] / 2, handleOffset: handleOffset };
200 };
201 Handle.prototype.removeDocumentEventListeners = function () {
202 document.removeEventListener("mousemove", this.handleHandleMovement);
203 document.removeEventListener("mouseup", this.endHandleMovement);
204 document.removeEventListener("touchmove", this.handleHandleTouchMovement);
205 document.removeEventListener("touchend", this.endHandleTouchMovement);
206 document.removeEventListener("touchcancel", this.endHandleTouchMovement);
207 };
208 Handle.displayName = "".concat(props_1.DISPLAYNAME_PREFIX, ".SliderHandle");
209 return Handle;
210}(common_1.AbstractPureComponent2));
211exports.Handle = Handle;
212//# sourceMappingURL=handle.js.map
\No newline at end of file