react-circle-slider-hooked-17
Version:
Circle Slider UI component for React
240 lines (208 loc) • 8.15 kB
JavaScript
'use strict';
Object.defineProperty(exports, '__esModule', { value: true });
function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'default' in ex) ? ex['default'] : ex; }
var React = require('react');
var React__default = _interopDefault(React);
function polarToCartesian(centerX, centerY, radius, angleInDegrees) {
var angleInRadians = (angleInDegrees - 90) * Math.PI / 180.0;
return {
x: centerX + radius * Math.cos(angleInRadians),
y: centerY + radius * Math.sin(angleInRadians)
};
}
function buildPath(_a) {
var cx = _a.cx,
cy = _a.cy,
radius = _a.radius,
startAngle = _a.startAngle,
endAngle = _a.endAngle,
thickness = _a.thickness;
var start = polarToCartesian(cx, cy, radius, endAngle);
var end = polarToCartesian(cx, cy, radius, startAngle);
var largeArcFlag = endAngle - startAngle <= 180 ? "0" : "1";
var cutoutRadius = radius - thickness;
var start2 = polarToCartesian(cx, cy, cutoutRadius, endAngle);
var end2 = polarToCartesian(cx, cy, cutoutRadius, startAngle);
return ["M", start.x, start.y, "A", radius, radius, 0, largeArcFlag, 0, end.x, end.y, "L", cx, cy, "Z", "M", start2.x, start2.y, "A", cutoutRadius, cutoutRadius, 0, largeArcFlag, 0, end2.x, end2.y, "L", cx, cy, "Z"].join(" ");
}
var THRESHOLD = 0.01;
var CircleSlider = function CircleSlider(_a) {
var _b = _a.value,
value = _b === void 0 ? 0 : _b,
_c = _a.size,
size = _c === void 0 ? 180 : _c,
_d = _a.stepSize,
stepSize = _d === void 0 ? 1 : _d,
_e = _a.min,
min = _e === void 0 ? 0 : _e,
_f = _a.max,
max = _f === void 0 ? 100 : _f,
_g = _a.circleWidth,
circleWidth = _g === void 0 ? 5 : _g,
_h = _a.circleColor,
circleColor = _h === void 0 ? "#e9eaee" : _h,
_j = _a.progressWidth,
progressWidth = _j === void 0 ? 10 : _j,
_k = _a.progressColor,
progressColor = _k === void 0 ? "#007aff" : _k,
_l = _a.knobColor,
knobColor = _l === void 0 ? "#e9eaee" : _l,
_m = _a.knobRadius,
knobRadius = _m === void 0 ? 10 : _m,
_o = _a.disabled,
disabled = _o === void 0 ? false : _o,
_p = _a.shadow,
shadow = _p === void 0 ? true : _p,
_q = _a.showTooltip,
showTooltip = _q === void 0 ? false : _q,
_r = _a.valuePrefix,
valuePrefix = _r === void 0 ? "" : _r,
_s = _a.valueSuffix,
valueSuffix = _s === void 0 ? "" : _s,
_t = _a.tooltipSize,
tooltipSize = _t === void 0 ? 32 : _t,
_u = _a.tooltipColor,
tooltipColor = _u === void 0 ? "#333" : _u,
onChange = _a.onChange,
className = _a.className; // takes care of min, max and stepSize
var formatValue = function formatValue(input) {
return Math.round((input || (value < min ? min : value > max ? max : value)) / stepSize) * stepSize;
};
var valueToAngle = function valueToAngle() {
var newAngle = Math.round((formatValue() - min) / (max - min) * 360);
return newAngle === 360 ? newAngle - THRESHOLD : newAngle % 360;
};
var angleToValue = function angleToValue(newAngle) {
return formatValue(newAngle / (360 / (max - min))) + min;
};
var svgRef = React.useRef();
var prevX = React.useRef(); // necessary since functional component
var updateSliderFromEvent = function updateSliderFromEvent(event) {
var rectSize = svgRef.current.getBoundingClientRect();
var rectCenter = rectSize.width / 2;
var x = event.clientX - rectSize.left - rectCenter;
var y = event.clientY - rectSize.top - rectCenter;
var angleBetweenTwoVectors = Math.atan2(y, x);
var newAngle = Math.round(angleBetweenTwoVectors * 180 / Math.PI) + 90;
if (x < 0 && y < 0) {
newAngle += 360;
} // prevent jumping from "< 360 to > 0" and "> 0 to < 360"
if (y < 0) {
if (prevX.current < 0 && x > 0) {
newAngle = 360 - THRESHOLD; // can't go over 360
} else if (prevX.current > 0 && x < 0) {
newAngle = THRESHOLD; // can't go lower than 0
} else {
prevX.current = x;
}
} else {
prevX.current = x;
}
onChange(angleToValue(newAngle));
}; // mouse event handlers
// --------------------
// react event
var handleMouseDown = function handleMouseDown(event) {
event.preventDefault();
svgRef.current.addEventListener("mousemove", handleMouseMove);
svgRef.current.addEventListener("mouseup", handleMouseUp);
}; // regular dom event
var handleMouseMove = function handleMouseMove(event) {
event.preventDefault();
updateSliderFromEvent(event);
}; // regular dom event
var handleMouseUp = function handleMouseUp(event) {
event.preventDefault();
svgRef.current.removeEventListener("mousemove", handleMouseMove);
svgRef.current.removeEventListener("mouseup", handleMouseUp);
}; // touch event handlers
// --------------------
// react event
var handleTouchStart = function handleTouchStart() {
svgRef.current.addEventListener("touchmove", handleTouchMove);
svgRef.current.addEventListener("touchend", handleTouchUp);
}; // regular dom event
var handleTouchMove = function handleTouchMove(event) {
var targetTouches = event.targetTouches;
var currentTouch = targetTouches.item(targetTouches.length - 1);
if (currentTouch) {
updateSliderFromEvent(currentTouch);
}
}; // regular dom event
var handleTouchUp = function handleTouchUp() {
svgRef.current.removeEventListener("touchmove", handleTouchMove);
svgRef.current.removeEventListener("touchend", handleTouchUp);
};
var center = size / 2; // if there is a shadow we need to draw a bit smaller to not cut if off
var radius = center - Math.max(circleWidth, progressWidth, knobRadius * 2) / 2 - (shadow ? 10 : 0);
var progressPath = buildPath({
cx: center,
cy: center,
radius: radius + progressWidth / 2,
startAngle: 0,
endAngle: valueToAngle(),
thickness: progressWidth
});
var knobCenter = polarToCartesian(center, center, radius, valueToAngle());
return /*#__PURE__*/React__default.createElement("svg", {
ref: svgRef,
className: className,
height: size,
width: size,
viewBox: "0 0 " + size + " " + size,
onMouseDown: disabled ? undefined : handleMouseDown,
onTouchStart: disabled ? undefined : handleTouchStart,
style: {
boxSizing: "border-box",
touchAction: "none"
}
}, /*#__PURE__*/React__default.createElement("circle", {
style: {
fill: "none",
stroke: circleColor,
strokeWidth: circleWidth
},
r: radius,
cx: center,
cy: center
}), /*#__PURE__*/React__default.createElement("path", {
style: {
fill: progressColor,
fillRule: "evenodd",
stroke: "none"
},
d: progressPath
}), shadow && /*#__PURE__*/React__default.createElement("filter", {
id: "dropShadow",
filterUnits: "userSpaceOnUse"
}, /*#__PURE__*/React__default.createElement("feGaussianBlur", {
"in": "SourceAlpha",
stdDeviation: "3"
}), /*#__PURE__*/React__default.createElement("feOffset", {
dx: "2",
dy: "2"
}), /*#__PURE__*/React__default.createElement("feComponentTransfer", null, /*#__PURE__*/React__default.createElement("feFuncA", {
type: "linear",
slope: "0.3"
})), /*#__PURE__*/React__default.createElement("feMerge", null, /*#__PURE__*/React__default.createElement("feMergeNode", null), /*#__PURE__*/React__default.createElement("feMergeNode", {
"in": "SourceGraphic"
}))), /*#__PURE__*/React__default.createElement("circle", {
style: {
cursor: disabled ? "not-allowed" : "pointer",
fill: knobColor
},
filter: shadow ? "url(#dropShadow)" : "none",
r: knobRadius,
cx: knobCenter.x,
cy: knobCenter.y
}), showTooltip && /*#__PURE__*/React__default.createElement("text", {
x: size / 2,
y: size / 2 + tooltipSize / 3,
textAnchor: "middle",
fontSize: tooltipSize,
fontFamily: "Arial",
fill: tooltipColor
}, valuePrefix, formatValue(), valueSuffix));
};
exports.CircleSlider = CircleSlider;
//# sourceMappingURL=index.js.map