chakra-ui
Version:
Responsive and accessible React UI components built with React and Emotion
325 lines (276 loc) • 9.69 kB
JavaScript
import _slicedToArray from "@babel/runtime/helpers/slicedToArray";
import _objectWithoutProperties from "@babel/runtime/helpers/objectWithoutProperties";
import _extends from "@babel/runtime/helpers/extends";
/** @jsx jsx */
import { jsx } from "@emotion/core";
import { forwardRef, useRef, useState, useCallback, createContext, useContext } from "react";
import Box from "../Box";
import useSliderStyle from "./styles";
import PseudoBox from "../PseudoBox";
import { mergeRefs } from "../utils";
export function valueToPercent(value, min, max) {
return (value - min) * 100 / (max - min);
}
export function percentToValue(percent, min, max) {
return (max - min) * percent + min;
}
function makeValuePrecise(value, step) {
var stepDecimalPart = step.toString().split(".")[1];
var stepPrecision = stepDecimalPart ? stepDecimalPart.length : 0;
return Number(value.toFixed(stepPrecision));
}
export function roundValueToStep(value, step) {
return makeValuePrecise(Math.round(value / step) * step, step);
}
function clampValue(val, min, max) {
if (val > max) {
return max;
}
if (val < min) {
return min;
}
return val;
} ////////////////////////////////////////////////////////////////
export var SliderThumb = forwardRef(function (props, _ref) {
var _useSliderContext = useSliderContext(),
thumbRef = _useSliderContext.thumbRef,
isDisabled = _useSliderContext.isDisabled,
onFocus = _useSliderContext.onFocus,
onKeyDown = _useSliderContext.onThumbKeyDown,
min = _useSliderContext.min,
max = _useSliderContext.max,
valueText = _useSliderContext.valueText,
orientation = _useSliderContext.orientation,
trackPercent = _useSliderContext.trackPercent,
size = _useSliderContext.size,
color = _useSliderContext.color,
value = _useSliderContext.value,
ariaLabelledBy = _useSliderContext.ariaLabelledBy;
var _useSliderStyle = useSliderStyle({
trackPercent: trackPercent,
orientation: orientation,
size: size,
color: color
}),
thumbStyle = _useSliderStyle.thumbStyle;
return jsx(PseudoBox, _extends({
"data-slider-thumb": "",
onFocus: onFocus,
ref: function ref(node) {
return mergeRefs([thumbRef, _ref], node);
},
role: "slider",
tabIndex: isDisabled ? undefined : 0,
"aria-disabled": isDisabled,
"aria-valuemin": min,
"aria-valuetext": valueText,
"aria-orientation": orientation,
"aria-valuenow": value,
"aria-valuemax": max,
"aria-labelledby": ariaLabelledBy,
onKeyDown: onKeyDown
}, thumbStyle, props));
}); ////////////////////////////////////////////////////////////////
export var SliderTrack = function SliderTrack(props) {
var _useSliderContext2 = useSliderContext(),
trackRef = _useSliderContext2.trackRef,
isDisabled = _useSliderContext2.isDisabled,
context = _objectWithoutProperties(_useSliderContext2, ["trackRef", "isDisabled"]);
var _useSliderStyle2 = useSliderStyle(context),
trackStyle = _useSliderStyle2.trackStyle;
return jsx(Box, _extends({
"data-slider-track": "",
"aria-disabled": isDisabled,
ref: trackRef
}, trackStyle, props));
}; ////////////////////////////////////////////////////////////////
export var SliderFilledTrack = function SliderFilledTrack(props) {
var _useSliderContext3 = useSliderContext(),
isDisabled = _useSliderContext3.isDisabled,
context = _objectWithoutProperties(_useSliderContext3, ["isDisabled"]);
var _useSliderStyle3 = useSliderStyle(context),
filledTrackStyle = _useSliderStyle3.filledTrackStyle;
return jsx(PseudoBox, _extends({
"aria-disabled": isDisabled,
"data-slider-filled-track": ""
}, filledTrackStyle, props));
}; ////////////////////////////////////////////////////////////////
var SliderContext = createContext();
var useSliderContext = function useSliderContext() {
return useContext(SliderContext);
};
var Slider = forwardRef(function (_ref2, ref) {
var controlledValue = _ref2.value,
defaultValue = _ref2.defaultValue,
onChange = _ref2.onChange,
onKeyDown = _ref2.onKeyDown,
onFocus = _ref2.onFocus,
_onBlur = _ref2.onBlur,
onMouseDown = _ref2.onMouseDown,
isDisabled = _ref2.isDisabled,
_ref2$max = _ref2.max,
max = _ref2$max === void 0 ? 100 : _ref2$max,
_ref2$min = _ref2.min,
min = _ref2$min === void 0 ? 0 : _ref2$min,
_ref2$step = _ref2.step,
step = _ref2$step === void 0 ? 1 : _ref2$step,
ariaLabelledBy = _ref2["aria-labelledby"],
ariaLabel = _ref2["aria-label"],
ariaValueText = _ref2["aria-valuetext"],
_ref2$orientation = _ref2.orientation,
orientation = _ref2$orientation === void 0 ? "horizontal" : _ref2$orientation,
getAriaValueText = _ref2.getAriaValueText,
_ref2$size = _ref2.size,
size = _ref2$size === void 0 ? "md" : _ref2$size,
_ref2$color = _ref2.color,
color = _ref2$color === void 0 ? "blue" : _ref2$color,
name = _ref2.name,
id = _ref2.id,
children = _ref2.children,
rest = _objectWithoutProperties(_ref2, ["value", "defaultValue", "onChange", "onKeyDown", "onFocus", "onBlur", "onMouseDown", "isDisabled", "max", "min", "step", "aria-labelledby", "aria-label", "aria-valuetext", "orientation", "getAriaValueText", "size", "color", "name", "id", "children"]);
var _useRef = useRef(controlledValue != null),
isControlled = _useRef.current;
var _useState = useState(defaultValue || 0),
_useState2 = _slicedToArray(_useState, 2),
value = _useState2[0],
setValue = _useState2[1];
var _value = isControlled ? controlledValue : value;
var actualValue = clampValue(_value, min, max);
var trackPercent = valueToPercent(actualValue, min, max);
var _useSliderStyle4 = useSliderStyle({
trackPercent: trackPercent,
orientation: orientation,
size: size,
color: color
}),
rootStyle = _useSliderStyle4.rootStyle;
var trackRef = useRef();
var thumbRef = useRef();
var getNewValue = function getNewValue(event) {
if (trackRef.current) {
var _trackRef$current$get = trackRef.current.getBoundingClientRect(),
left = _trackRef$current$get.left,
width = _trackRef$current$get.width;
var clientX = event.clientX;
var diffX = clientX - left;
var percent = diffX / width;
var newValue = percentToValue(percent, min, max);
if (step) {
newValue = roundValueToStep(newValue, step);
}
newValue = clampValue(newValue, min, max);
return newValue;
}
};
var updateValue = useCallback(function (newValue) {
if (!isControlled) {
setValue(newValue);
}
if (onChange) {
onChange(newValue);
}
}, [isControlled, onChange]);
var handleThumbKeyDown = function handleThumbKeyDown(event) {
var flag = false;
var newValue;
var tenSteps = (max - min) / 10;
switch (event.key) {
case "ArrowLeft":
case "ArrowDown":
newValue = actualValue - step;
flag = true;
break;
case "ArrowRight":
case "ArrowUp":
newValue = actualValue + step;
flag = true;
break;
case "PageDown":
newValue = actualValue - tenSteps;
flag = true;
break;
case "PageUp":
newValue = actualValue + tenSteps;
flag = true;
break;
case "Home":
newValue = min;
flag = true;
break;
case "End":
newValue = max;
flag = true;
break;
default:
return;
}
if (flag) {
event.preventDefault();
event.stopPropagation();
}
if (step) {
newValue = roundValueToStep(newValue, step);
}
newValue = clampValue(newValue, min, max);
updateValue(newValue);
onKeyDown && onKeyDown(event);
};
var handleMouseUp = function handleMouseUp() {
document.body.removeEventListener("mousemove", handleMouseMove);
document.body.removeEventListener("mouseup", handleMouseUp);
}; // TODO: Optimize this mouseMove event
var handleMouseMove = function handleMouseMove(event) {
var newValue = getNewValue(event);
updateValue(newValue);
};
var handleMouseDown = function handleMouseDown(event) {
if (isDisabled) return;
onMouseDown && onMouseDown(event);
event.preventDefault();
var newValue = getNewValue(event);
if (newValue !== actualValue) {
updateValue(newValue);
}
document.body.addEventListener("mousemove", handleMouseMove);
document.body.addEventListener("mouseup", handleMouseUp);
thumbRef.current && thumbRef.current.focus();
};
var valueText = getAriaValueText ? getAriaValueText(actualValue) : ariaValueText;
var context = {
trackRef: trackRef,
thumbRef: thumbRef,
onThumbKeyDown: handleThumbKeyDown,
onFocus: onFocus,
trackPercent: trackPercent,
ariaLabelledBy: ariaLabelledBy,
orientation: orientation,
isDisabled: isDisabled,
size: size,
color: color,
min: min,
max: max,
valueText: valueText,
value: actualValue
};
return jsx(SliderContext.Provider, {
value: context
}, jsx(Box, _extends({
role: "presentation",
tabIndex: "-1",
onMouseDown: handleMouseDown,
onMouseLeave: handleMouseUp,
onBlur: function onBlur(event) {
handleMouseUp();
_onBlur && _onBlur(event);
},
py: 3,
"aria-disabled": isDisabled,
ref: ref
}, rootStyle, rest), children, jsx("input", {
type: "hidden",
value: actualValue,
name: name,
id: id
})));
});
export default Slider;