UNPKG

chayns-components

Version:

A set of beautiful React components for developing chayns® applications.

238 lines (232 loc) 8.65 kB
"use strict"; var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); exports.__esModule = true; exports.default = void 0; var _clsx = _interopRequireDefault(require("clsx")); var _propTypes = _interopRequireDefault(require("prop-types")); var _react = _interopRequireWildcard(require("react")); require("./SliderButton.css"); function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function (nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); } function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; } /** * @component */ const defaultItems = [{ id: 0, text: 'Auf' }, { id: 1, text: 'Stopp' }, { id: 2, text: 'Zu' }]; /** * A linear set of buttons which are mutually exclusive. */ const SliderButton = _ref => { let { className, style, items = defaultItems, onChange, onDragStop, onDragStart, selectedItemId = 0, disabled = false } = _ref; const [markerPosX, setMarkerPosX] = (0, _react.useState)(0); const [dragStartPosX, setDragStartPosX] = (0, _react.useState)(null); const [dragStartMarkerPosX, setDragStartMarkerPosX] = (0, _react.useState)(null); const [lastSelectedIndex, setLastSelectedIndex] = (0, _react.useState)(selectedItemId); const sliderButtonRef = (0, _react.useRef)(); const sliderButton = sliderButtonRef && sliderButtonRef.current; const firstItemRef = (0, _react.useRef)(); let firstItem = firstItemRef && firstItemRef.current; const markerRef = (0, _react.useRef)(); let marker = markerRef && markerRef.current; const handleChange = newIndex => { if (newIndex !== lastSelectedIndex) { setLastSelectedIndex(newIndex); if (onChange) { onChange(items[newIndex]); } } }; const getHoveredItemIndex = function (markerPositionX) { if (markerPositionX === void 0) { markerPositionX = markerPosX; } if (firstItem && firstItem.clientWidth > 0) { const markerHalfPosX = markerPositionX + firstItem.clientWidth / 2; return Math.floor(markerHalfPosX / firstItem.clientWidth); } return 0; }; const setMarkerIndex = index => { if (firstItem && index > -1 && index < items.length) { const newMarkerPosX = index * firstItem.clientWidth; // Element.animate() does not work on iOS, so we need transition setLastSelectedIndex(index); markerRef.current.style.left = `${newMarkerPosX}px`; setMarkerPosX(newMarkerPosX); if (!marker.style.transition) { requestAnimationFrame(() => { marker.style.transition = 'left 0.2s cubic-bezier(0.42, 0, 0.29, 1.36)'; }); } } }; const startDrag = posX => { if (!disabled) { chayns.disallowRefreshScroll(); // Element.animate() does not work on iOS, so we need transition // Transition have to be removed if the user drags the marker marker.style.transition = ''; setDragStartPosX(posX); setDragStartMarkerPosX(markerPosX); if (onDragStart) { onDragStart(); } } }; const stopDrag = () => { if (dragStartPosX) { chayns.allowRefreshScroll(); setDragStartPosX(null); setDragStartMarkerPosX(null); const hoveredItemIndex = getHoveredItemIndex(); setMarkerIndex(hoveredItemIndex); if (onDragStop) { onDragStop(items[hoveredItemIndex]); } } }; const handleMovement = posX => { if (dragStartPosX) { const maxMarkerPosX = sliderButton && firstItem ? sliderButton.clientWidth - firstItem.clientWidth : 0; let newMarkerPosX = dragStartMarkerPosX + posX - dragStartPosX; if (newMarkerPosX < 0) { newMarkerPosX = 0; } else if (newMarkerPosX > maxMarkerPosX) { newMarkerPosX = maxMarkerPosX; } const newSelectedIndex = getHoveredItemIndex(newMarkerPosX); handleChange(newSelectedIndex); markerRef.current.style.left = `${newMarkerPosX}px`; setMarkerPosX(newMarkerPosX); } }; (0, _react.useEffect)(() => { // eslint-disable-next-line react-hooks/exhaustive-deps firstItem = firstItemRef.current; // eslint-disable-next-line react-hooks/exhaustive-deps marker = markerRef.current; if (selectedItemId) { const i = items.findIndex(item => item.id === selectedItemId); setMarkerIndex(i); } }, []); (0, _react.useEffect)(() => { if (!dragStartPosX) { const i = items.findIndex(item => item.id === selectedItemId); setMarkerIndex(i); } // eslint-disable-next-line react-hooks/exhaustive-deps }, [selectedItemId]); (0, _react.useEffect)(() => { const listener = [{ type: 'mousemove', cb: ev => handleMovement(ev.clientX) }, { type: 'touchmove', cb: ev => handleMovement(ev.touches[0].clientX) }, { type: 'mouseup', cb: () => stopDrag() }, { type: 'touchend', cb: () => stopDrag() }]; listener.forEach(lst => window.addEventListener(lst.type, lst.cb)); return () => { listener.forEach(lst => window.removeEventListener(lst.type, lst.cb)); }; }); const hoveredItemIndex = getHoveredItemIndex(); return /*#__PURE__*/_react.default.createElement("div", { className: (0, _clsx.default)('sliderButton', className, disabled && 'sliderButton--disabled'), style: style, ref: sliderButtonRef }, items.map((item, i) => /*#__PURE__*/_react.default.createElement("div", { key: item.id, className: "sliderButton__item button button--disabled" // disabled to remove hover animation , ref: ref => { if (i === 0) { firstItemRef.current = ref; } }, onClick: () => { if (!disabled) { setMarkerIndex(i); handleChange(i); } } }, /*#__PURE__*/_react.default.createElement("div", { className: "sliderButton__item__content" }, item.text))), /*#__PURE__*/_react.default.createElement("div", { className: "sliderButton__item__marker button", onMouseDown: ev => { if (!chayns.env.isMobile) { startDrag(ev.clientX); } }, onTouchStart: ev => startDrag(ev.touches[0].clientX), ref: markerRef }, /*#__PURE__*/_react.default.createElement("div", { className: "sliderButton__item__content" }, items[hoveredItemIndex].text))); }; SliderButton.propTypes = { /** * A classname string that will be applied to the container element. */ className: _propTypes.default.string, /** * A React style objec that will be applied to the container element. */ style: _propTypes.default.objectOf(_propTypes.default.oneOfType([_propTypes.default.string, _propTypes.default.number])), /** * An array of option items. */ items: _propTypes.default.arrayOf(_propTypes.default.shape({ id: _propTypes.default.oneOfType([_propTypes.default.string, _propTypes.default.number]), text: _propTypes.default.oneOfType([_propTypes.default.string, _propTypes.default.node]) })), /** * A callback that is invoked when the selection changes. */ onChange: _propTypes.default.func, /** * A callback that is invoked when the user starts dragging the control. */ onDragStop: _propTypes.default.func, /** * A callback that is invoked when the user stops dragging. */ onDragStart: _propTypes.default.func, /** * The `id` of the item that should be selected. */ selectedItemId: _propTypes.default.number, /** * Wether the `SliderButton` should ignore user interaction and be rendered * in a disabled style. */ disabled: _propTypes.default.bool }; SliderButton.displayName = 'SliderButton'; var _default = SliderButton; exports.default = _default; //# sourceMappingURL=SliderButton.js.map