@kiwicom/orbit-components
Version:
Orbit-components is a React component library which provides developers with the easiest possible way of building Kiwi.com's products.
321 lines (319 loc) • 12.7 kB
JavaScript
"use strict";
"use client";
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault").default;
var _interopRequireWildcard = require("@babel/runtime/helpers/interopRequireWildcard").default;
exports.__esModule = true;
exports.default = void 0;
var React = _interopRequireWildcard(require("react"));
var _clsx = _interopRequireDefault(require("clsx"));
var _Text = _interopRequireDefault(require("../Text"));
var _Heading = _interopRequireDefault(require("../Heading"));
var _Stack = _interopRequireDefault(require("../Stack"));
var _Hide = _interopRequireDefault(require("../Hide"));
var _Handle = _interopRequireDefault(require("./components/Handle"));
var _Bar = _interopRequireDefault(require("./components/Bar"));
var _consts = _interopRequireDefault(require("./consts"));
var _Histogram = _interopRequireDefault(require("./components/Histogram"));
var _utils = require("./utils");
var _useTheme = _interopRequireDefault(require("../hooks/useTheme"));
const Slider = ({
defaultValue = _consts.default.VALUE,
maxValue = _consts.default.MAX,
minValue = _consts.default.MIN,
step = _consts.default.STEP,
onChange,
onChangeAfter,
onChangeBefore,
ariaValueText,
ariaLabel,
label,
histogramData,
histogramLoading,
histogramDescription,
histogramLoadingText,
valueDescription,
id,
dataTest
}) => {
const bar = React.useRef(null);
const [value, setValue] = React.useState(defaultValue);
const valueRef = React.useRef(value);
const defaultRef = React.useRef(defaultValue);
const handleIndex = React.useRef(null);
const [focused, setFocused] = React.useState(false);
const theme = (0, _useTheme.default)();
const {
rtl
} = theme;
const updateValue = newValue => {
valueRef.current = newValue;
setValue(newValue);
};
React.useEffect(() => {
const newValue = Array.isArray(defaultValue) ? defaultValue.map(item => Number(item)) : Number(defaultValue);
if ((0, _utils.isNotEqual)(defaultValue, defaultRef.current)) {
defaultRef.current = newValue;
updateValue(newValue);
}
}, [defaultValue]);
const handleKeyDown = event => {
if (event.ctrlKey || event.shiftKey || event.altKey) return;
// Return early if not a navigation key
if (!["ArrowUp", "ArrowDown", "ArrowLeft", "ArrowRight", "Home", "End"].includes(event.key)) return;
switch (event.key) {
case "ArrowUp":
(0, _utils.pauseEvent)(event);
if (onChange) {
(0, _utils.injectCallbackAndSetState)(updateValue, onChange, (0, _utils.moveValueByExtraStep)(valueRef.current, maxValue, minValue, step, handleIndex.current, step));
}
break;
case "ArrowDown":
(0, _utils.pauseEvent)(event);
if (onChange) {
(0, _utils.injectCallbackAndSetState)(updateValue, onChange, (0, _utils.moveValueByExtraStep)(valueRef.current, maxValue, minValue, step, handleIndex.current, -step));
}
break;
case "ArrowRight":
{
const switchStep = rtl ? -step : step;
(0, _utils.pauseEvent)(event);
if (onChange) {
(0, _utils.injectCallbackAndSetState)(updateValue, onChange, (0, _utils.moveValueByExtraStep)(valueRef.current, maxValue, minValue, step, handleIndex.current, switchStep));
}
}
break;
case "ArrowLeft":
{
const switchStep = rtl ? step : -step;
(0, _utils.pauseEvent)(event);
if (onChange) {
(0, _utils.injectCallbackAndSetState)(updateValue, onChange, (0, _utils.moveValueByExtraStep)(valueRef.current, maxValue, minValue, step, handleIndex.current, switchStep));
}
}
break;
case "Home":
(0, _utils.pauseEvent)(event);
if (onChange) {
(0, _utils.injectCallbackAndSetState)(updateValue, onChange, (0, _utils.moveValueByExtraStep)(valueRef.current, maxValue, minValue, step, handleIndex.current, 0, minValue));
}
break;
case "End":
(0, _utils.pauseEvent)(event);
if (onChange) {
(0, _utils.injectCallbackAndSetState)(updateValue, onChange, (0, _utils.moveValueByExtraStep)(valueRef.current, maxValue, minValue, step, handleIndex.current, 0, maxValue));
}
break;
default:
}
};
const handleBlur = () => {
setFocused(false);
window.removeEventListener("keydown", handleKeyDown);
window.removeEventListener("focusout", handleBlur);
if (onChangeAfter) {
(0, _utils.injectCallbackAndSetState)(updateValue, onChangeAfter, valueRef.current);
}
};
const handleOnFocus = i => event => {
handleIndex.current = i;
setFocused(true);
(0, _utils.pauseEvent)(event.nativeEvent);
window.addEventListener("keydown", handleKeyDown);
window.addEventListener("focusout", handleBlur);
if (onChangeBefore) {
(0, _utils.injectCallbackAndSetState)(updateValue, onChangeBefore, value);
}
};
const handleMove = newValue => {
if (newValue != null) {
if (Array.isArray(value)) {
return (0, _utils.constrainRangeValue)(valueRef.current, (0, _utils.alignValue)(maxValue, minValue, step, newValue), Number(handleIndex.current));
}
return (0, _utils.alignValue)(maxValue, minValue, step, newValue);
}
return null;
};
const handleBarMouseDown = event => {
handleIndex.current = null;
const newValue = (0, _utils.calculateValueFromPosition)({
histogramData,
histogramLoading,
maxValue,
minValue,
handleIndex: handleIndex.current,
bar,
rtl,
value,
pageX: event.pageX,
throughClick: true
});
if (newValue) {
if (Array.isArray(value)) {
const index = (0, _utils.findClosestKey)(newValue, value);
const replacedValue = (0, _utils.constrainRangeValue)(value, (0, _utils.alignValue)(maxValue, minValue, step, newValue), index || 0);
if (onChangeBefore) (0, _utils.injectCallbackAndSetState)(updateValue, onChangeBefore, value);
if (onChange) (0, _utils.injectCallbackAndSetState)(updateValue, onChange, replacedValue);
if (onChangeAfter) (0, _utils.injectCallbackAndSetState)(updateValue, onChangeAfter, replacedValue);
} else {
const alignedValue = (0, _utils.alignValue)(maxValue, minValue, step, newValue);
if (onChangeBefore) (0, _utils.injectCallbackAndSetState)(updateValue, onChangeBefore, value);
if (onChange) (0, _utils.injectCallbackAndSetState)(updateValue, onChange, alignedValue);
if (onChangeAfter) (0, _utils.injectCallbackAndSetState)(updateValue, onChangeAfter, alignedValue);
}
}
};
const handleMouseMove = event => {
const newValue = (0, _utils.calculateValueFromPosition)({
histogramData,
histogramLoading,
maxValue,
minValue,
handleIndex: handleIndex.current,
bar,
rtl,
value,
pageX: event.pageX
});
(0, _utils.pauseEvent)(event);
(0, _utils.injectCallbackAndSetState)(updateValue, onChange, handleMove(newValue));
};
const handleMouseUp = () => {
setFocused(false);
window.removeEventListener("mousemove", handleMouseMove);
window.removeEventListener("mouseup", handleMouseUp);
if (onChangeAfter) {
(0, _utils.injectCallbackAndSetState)(updateValue, onChangeAfter, valueRef.current);
}
};
const handleMouseDown = i => event => {
// just allow left-click
if (event.button === 0 && event.buttons !== 2) {
setFocused(true);
handleIndex.current = i;
window.addEventListener("mousemove", handleMouseMove);
window.addEventListener("mouseup", handleMouseUp);
(0, _utils.pauseEvent)(event.nativeEvent);
if (onChangeBefore) {
(0, _utils.injectCallbackAndSetState)(updateValue, onChangeBefore, value);
}
}
};
const handleOnTouchMove = event => {
if (event.touches.length > 1) return;
const newValue = (0, _utils.calculateValueFromPosition)({
histogramData,
histogramLoading,
maxValue,
minValue,
handleIndex: handleIndex.current,
bar,
rtl,
value,
pageX: event.touches[0]?.pageX || 0
});
(0, _utils.pauseEvent)(event);
(0, _utils.injectCallbackAndSetState)(updateValue, onChange, handleMove(newValue));
};
const handleTouchEnd = () => {
setFocused(false);
window.removeEventListener("touchmove", handleOnTouchMove);
window.removeEventListener("touchend", handleTouchEnd);
if (onChangeAfter) {
(0, _utils.injectCallbackAndSetState)(updateValue, onChangeAfter, valueRef.current);
}
};
const handleOnTouchStart = i => event => {
if (event.touches.length <= 1) {
setFocused(true);
handleIndex.current = i;
window.addEventListener("touchmove", handleOnTouchMove, {
passive: false
});
window.addEventListener("touchend", handleTouchEnd);
(0, _utils.stopPropagation)(event.nativeEvent);
if (onChangeBefore) {
(0, _utils.injectCallbackAndSetState)(updateValue, onChangeBefore, value);
}
}
};
const renderHandle = i => {
const key = i && encodeURIComponent(i.toString());
const index = i || 0;
return /*#__PURE__*/React.createElement(_Handle.default, {
onTop: handleIndex.current === i,
valueMax: maxValue,
valueMin: minValue,
onMouseDown: handleMouseDown(index),
onFocus: handleOnFocus(index),
onTouchStart: handleOnTouchStart(index),
value: valueRef.current,
ariaValueText: ariaValueText,
ariaLabel: ariaLabel,
hasHistogram: histogramLoading || !!histogramData,
index: index,
key: key,
dataTest: `SliderHandle-${index}`
});
};
const renderHandles = () => Array.isArray(value) ? value.map((_valueNow, index) => renderHandle(index)) : renderHandle();
const renderSliderTexts = React.useCallback(biggerSpace => {
if (!(label || valueDescription || histogramDescription)) return null;
return /*#__PURE__*/React.createElement(_Stack.default, {
direction: "row",
spacing: "none",
spaceAfter: biggerSpace ? "medium" : "small"
}, (label || histogramDescription) && /*#__PURE__*/React.createElement(_Stack.default, {
direction: "column",
spacing: "none",
basis: "60%",
grow: true
}, label && /*#__PURE__*/React.createElement(_Heading.default, {
type: "title4"
}, label), valueDescription && /*#__PURE__*/React.createElement(_Text.default, {
type: "secondary",
size: "small"
}, valueDescription)), histogramDescription && /*#__PURE__*/React.createElement(_Stack.default, {
shrink: true,
justify: "end",
grow: false
}, /*#__PURE__*/React.createElement(_Text.default, {
type: "primary",
size: "small"
}, histogramDescription)));
}, [histogramDescription, label, valueDescription]);
if (histogramData) {
const properHistogramLength = (maxValue - minValue + step) / step;
if (histogramData.length !== properHistogramLength) {
console.warn(`Warning: Length of histogramData array is ${histogramData.length}, but should be ${properHistogramLength}. This will cause broken visuals of the whole Histogram.`);
}
}
const sortedValue = (0, _utils.sortArray)(valueRef.current);
const hasHistogram = histogramLoading || !!histogramData;
return /*#__PURE__*/React.createElement("div", {
className: "orbit-slider relative",
"data-test": dataTest,
id: id
}, hasHistogram ? /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(_Hide.default, {
on: ["smallMobile", "mediumMobile", "largeMobile"],
block: true
}, renderSliderTexts(true)), /*#__PURE__*/React.createElement("div", {
className: (0, _clsx.default)("pb-200 tb:w-[calc(100%+48px)] tb:absolute tb:-bottom-400 tb:-inset-x-600 tb:pt-300 tb:px-600 tb:pb-1200 tb:rounded-100 tb:transition-opacity tb:ease-in-out tb:duration-fast tb:bg-white-normal tb:shadow-level3", focused ? "tb:visible tb:opacity-100" : "tb:invisible tb:opacity-0")
}, renderSliderTexts(false), /*#__PURE__*/React.createElement(_Histogram.default, {
data: histogramData,
value: sortedValue,
min: minValue,
step: step,
loading: histogramLoading,
loadingText: histogramLoadingText
}))) : renderSliderTexts(true), /*#__PURE__*/React.createElement("div", {
className: "h-600 flex w-full items-center"
}, /*#__PURE__*/React.createElement(_Bar.default, {
ref: bar,
onMouseDown: handleBarMouseDown,
value: sortedValue,
max: maxValue,
min: minValue,
hasHistogram: hasHistogram
}), renderHandles()));
};
var _default = exports.default = Slider;