@chayns-components/core
Version:
A set of beautiful React components for developing your own applications with chayns.
408 lines (406 loc) • 16.4 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = void 0;
var _chaynsApi = require("chayns-api");
var _react = require("motion/react");
var _react2 = _interopRequireWildcard(require("react"));
var _element = require("../../hooks/element");
var _useKeyboardFocusHighlighting = require("../../hooks/useKeyboardFocusHighlighting");
var _ColorSchemeProvider = require("../color-scheme-provider/ColorSchemeProvider");
var _calculate = require("../../utils/calculate");
var _sliderButton = require("../../utils/sliderButton");
var _Icon = _interopRequireDefault(require("../icon/Icon"));
var _Popup = _interopRequireDefault(require("../popup/Popup"));
var _SliderButton = require("./SliderButton.styles");
var _styledComponents = require("styled-components");
var _useSliderButtonPopupKeyboard = require("./useSliderButtonPopupKeyboard");
var _useSliderButtonThumbKeyboard = require("./useSliderButtonThumbKeyboard");
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function (e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != typeof e && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (const t in e) "default" !== t && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]); return f; })(e, t); }
const SliderButton = ({
isDisabled,
isSecondary,
items,
onChange,
selectedButtonId,
isRounded = false,
shouldEnableKeyboardHighlighting
}) => {
const [dragRange, setDragRange] = (0, _react2.useState)({
left: 0,
right: 0
});
const [shownItemsCount, setShownItemsCount] = (0, _react2.useState)(items.length);
const [sliderSize, setSliderSize] = (0, _react2.useState)({
width: 0
});
const [currentId, setCurrentId] = (0, _react2.useState)('');
const [currentPopupId, setCurrentPopupId] = (0, _react2.useState)('');
const [currentIndex, setCurrentIndex] = (0, _react2.useState)(0);
const [isPopupOpen, setIsPopupOpen] = (0, _react2.useState)(false);
const sliderButtonRef = (0, _react2.useRef)(null);
const sliderButtonWrapperRef = (0, _react2.useRef)(null);
const popupRef = (0, _react2.useRef)(null);
const popupItemRefs = (0, _react2.useRef)([]);
const [scope, animate] = (0, _react.useAnimate)();
const theme = (0, _styledComponents.useTheme)();
const colorScheme = (0, _ColorSchemeProvider.useColorScheme)();
const shouldEnableKeyboardHighlightingEffective = shouldEnableKeyboardHighlighting ?? (colorScheme === null || colorScheme === void 0 ? void 0 : colorScheme.shouldEnableKeyboardHighlighting) ?? false;
const shouldShowKeyboardHighlighting = (0, _useKeyboardFocusHighlighting.useKeyboardFocusHighlighting)(shouldEnableKeyboardHighlightingEffective && !isDisabled);
const initialItemWidth = (0, _react2.useMemo)(() => (0, _calculate.calculateBiggestWidth)(items), [items]);
const elementSize = (0, _element.useElementSize)(sliderButtonRef);
(0, _react2.useEffect)(() => {
if (elementSize) setSliderSize(elementSize);
}, [elementSize]);
const setPopupId = (0, _react2.useCallback)(selectedId => {
const ids = items.slice(shownItemsCount - 1).map(({
id
}) => id);
const newId = ids.find(id => id === selectedId);
if (newId) {
setCurrentId('more');
setCurrentPopupId(newId);
return;
}
setCurrentId(selectedId);
}, [items, shownItemsCount]);
const isSliderBigger = (0, _react2.useMemo)(() => sliderSize && Math.floor(sliderSize.width / initialItemWidth) < items.length - 1, [initialItemWidth, items.length, sliderSize]);
const maxShownItemsCount = (0, _react2.useMemo)(() => {
let totalWidth = 0;
let count = 0;
while (count < items.length) {
const visibleItems = items.slice(0, count + 1);
const currentMaxWidth = (0, _calculate.calculateBiggestWidth)(visibleItems) + 8;
if (totalWidth + currentMaxWidth > sliderSize.width) break;
totalWidth += currentMaxWidth;
count++;
}
return count;
}, [items, sliderSize.width]);
const itemWidth = (0, _react2.useMemo)(() => {
const sliderWidth = (sliderSize === null || sliderSize === void 0 ? void 0 : sliderSize.width) || 0;
const itemCount = items.length || 1;
setShownItemsCount(isSliderBigger ? maxShownItemsCount : itemCount);
return sliderWidth / (isSliderBigger ? maxShownItemsCount : itemCount);
}, [isSliderBigger, items.length, maxShownItemsCount, sliderSize === null || sliderSize === void 0 ? void 0 : sliderSize.width]);
(0, _react2.useEffect)(() => {
if (sliderSize) {
const sliderWidth = itemWidth * (items.length - 1);
const count = Math.floor(sliderSize.width / itemWidth);
setDragRange({
left: 0,
right: isSliderBigger ? itemWidth * count : sliderWidth
});
}
}, [isSliderBigger, itemWidth, items.length, sliderSize]);
const animation = (0, _react2.useCallback)(async x => {
await animate(scope.current, {
x
}, {
type: 'tween',
duration: 0.2
});
}, [animate, scope]);
const setItemPosition = (0, _react2.useCallback)(index => {
setCurrentIndex(index);
void animation(itemWidth * index);
}, [animation, itemWidth]);
(0, _react2.useEffect)(() => {
if (typeof selectedButtonId === 'string') {
let index = items.findIndex(({
id
}) => id === selectedButtonId);
setCurrentId(selectedButtonId);
setPopupId(selectedButtonId);
if (items.length > shownItemsCount && index > shownItemsCount - 1) {
index = shownItemsCount - 1;
}
if (index >= 0) {
setItemPosition(index);
}
}
}, [animation, dragRange.right, isSliderBigger, itemWidth, items, selectedButtonId, setItemPosition, setPopupId, shownItemsCount]);
const handleClick = (0, _react2.useCallback)((id, index) => {
if (isDisabled) {
return;
}
if (currentIndex === index && items.length === 2) {
const otherItem = items.find((_, findIndex) => index !== findIndex);
if (!otherItem) return;
setPopupId(otherItem.id);
setItemPosition(items.indexOf(otherItem));
onChange === null || onChange === void 0 || onChange(otherItem.id);
return;
}
setPopupId(id);
if (typeof onChange === 'function' && id !== 'more') {
onChange(id);
}
if (popupRef.current) {
if (id === 'more') {
popupRef.current.show();
} else {
popupRef.current.hide();
}
}
setItemPosition(index);
}, [currentIndex, isDisabled, items, onChange, setItemPosition, setPopupId]);
const backgroundColor = (0, _react2.useMemo)(() => {
let color;
if (isSecondary) {
color = theme['202'];
} else {
color = theme.buttonBackgroundColor ?? theme['408'];
}
if (theme.buttonDesign === '2') {
color = `rgba(${theme['102-rgb'] ?? ''}, 0)`;
}
return color;
}, [isSecondary, theme]);
const thumbBackgroundColor = (0, _react2.useMemo)(() => {
let color;
if (isSecondary) {
color = theme['207'];
} else {
color = `rgba(${theme['405-rgb'] ?? ''}, 0.75)`;
}
if (theme.buttonDesign === '2') {
color = `rgba(${theme['102-rgb'] ?? ''}, 0)`;
}
return color;
}, [isSecondary, theme]);
const popupItems = (0, _react2.useMemo)(() => items.slice(shownItemsCount - 1), [items, shownItemsCount]);
const focusThumb = (0, _react2.useCallback)(() => {
var _sliderButtonRef$curr;
const thumb = (_sliderButtonRef$curr = sliderButtonRef.current) === null || _sliderButtonRef$curr === void 0 ? void 0 : _sliderButtonRef$curr.querySelector('[data-slider-button-thumb="true"]');
thumb === null || thumb === void 0 || thumb.focus();
}, []);
const {
handlePopupKeyDown
} = (0, _useSliderButtonPopupKeyboard.useSliderButtonPopupKeyboard)({
isPopupOpen,
popupItems,
currentPopupId,
shownItemsCount,
popupItemRefs,
popupRef,
focusThumb,
onSelectPopupItem: handleClick
});
const {
handleThumbKeyDown
} = (0, _useSliderButtonThumbKeyboard.useSliderButtonThumbKeyboard)({
currentId,
currentIndex,
shownItemsCount,
items,
onSelectThumbItem: handleClick
});
const buttons = (0, _react2.useMemo)(() => {
if (items.length > shownItemsCount) {
const newItems = items.slice(0, shownItemsCount - 1);
const otherItems = items.slice(shownItemsCount - 1);
const elements = newItems.map(({
id,
text
}, index) => /*#__PURE__*/_react2.default.createElement(_SliderButton.StyledSliderButtonItem, {
$isSecondary: isSecondary,
$width: itemWidth,
key: `slider-button-${id}`,
onClick: () => handleClick(id, index)
}, text));
const popupContent = otherItems.map(({
id,
text
}, popupIndex) => /*#__PURE__*/_react2.default.createElement(_SliderButton.StyledSliderButtonPopupContentItem, {
key: `slider-button-${id}`,
onClick: () => handleClick(id, newItems.length),
onKeyDown: handlePopupKeyDown,
ref: element => {
popupItemRefs.current[popupIndex] = element;
},
tabIndex: -1,
role: "button",
$isSelected: id === currentPopupId
}, text));
const id = 'more';
elements.push(/*#__PURE__*/_react2.default.createElement(_SliderButton.StyledSliderButtonItem, {
$isSecondary: isSecondary,
$width: itemWidth,
key: `slider-button-${id}`
}, /*#__PURE__*/_react2.default.createElement(_Popup.default, {
ref: popupRef,
onShow: () => {
setIsPopupOpen(true);
},
onHide: () => {
setIsPopupOpen(false);
popupItemRefs.current = [];
},
content: /*#__PURE__*/_react2.default.createElement(_SliderButton.StyledSliderButtonPopupContent, {
onKeyDown: handlePopupKeyDown
}, popupContent)
}, /*#__PURE__*/_react2.default.createElement(_Icon.default, {
icons: ['fa fa-ellipsis'],
color: "white"
}))));
return elements;
}
return items.map(({
id,
text
}) => /*#__PURE__*/_react2.default.createElement(_SliderButton.StyledSliderButtonItem, {
$isSecondary: isSecondary,
$width: itemWidth,
key: `slider-button-${id}`
}, text));
}, [currentPopupId, handleClick, handlePopupKeyDown, isSecondary, itemWidth, items, shownItemsCount]);
const pseudoButtons = (0, _react2.useMemo)(() => {
if (items.length > shownItemsCount) {
const newItems = items.slice(0, shownItemsCount - 1);
const elements = newItems.map(({
id,
text
}, index) => /*#__PURE__*/_react2.default.createElement(_SliderButton.StyledSliderButtonItem, {
$isSecondary: isSecondary,
$width: itemWidth,
key: `pseudo-slider-button-${id}`,
onClick: () => handleClick(id, index)
}, text));
const id = 'more';
elements.push(/*#__PURE__*/_react2.default.createElement(_SliderButton.StyledSliderButtonItem, {
$isSecondary: isSecondary,
$width: itemWidth,
key: `pseudo-slider-button-${id}`,
onClick: () => handleClick(id, newItems.length)
}, /*#__PURE__*/_react2.default.createElement(_Icon.default, {
icons: ['fa fa-ellipsis']
})));
return elements;
}
return items.map(({
id,
text
}, index) => /*#__PURE__*/_react2.default.createElement(_SliderButton.StyledSliderButtonItem, {
$isSecondary: isSecondary,
$width: itemWidth,
key: `pseudo-slider-button-${id}`,
onClick: () => handleClick(id, index)
}, text));
}, [handleClick, isSecondary, itemWidth, items, shownItemsCount]);
/**
* Creates an array with the snap points relative to the width of the items
*/
const snapPoints = (0, _react2.useMemo)(() => {
const points = [0];
for (let i = 1; i < items.length; i++) {
points.push(itemWidth * i);
}
return points;
}, [itemWidth, items.length]);
const handleDragStart = (0, _react2.useCallback)(() => {
void (0, _chaynsApi.setRefreshScrollEnabled)(false);
}, []);
const handleDragEnd = (0, _react2.useCallback)(() => {
void (0, _chaynsApi.setRefreshScrollEnabled)(true);
const position = (0, _sliderButton.getThumbPosition)({
scope,
itemWidth
});
if (!position) {
return;
}
const {
middle,
left
} = position;
let scrollLeft = 0;
if (sliderButtonWrapperRef.current) {
scrollLeft = sliderButtonWrapperRef.current.scrollLeft;
sliderButtonWrapperRef.current.scrollLeft = (0, _sliderButton.getNearestPoint)({
snapPoints,
position: middle,
scrollLeft: scrollLeft - left
}).nearestPoint;
}
const {
nearestIndex
} = (0, _sliderButton.getNearestPoint)({
snapPoints,
position: middle,
scrollLeft
});
const {
nearestPoint
} = (0, _sliderButton.getNearestPoint)({
snapPoints,
position: middle,
scrollLeft: 0
});
const hasMoreItems = items.length > shownItemsCount;
if (nearestPoint >= 0 && nearestIndex >= 0) {
void animation(nearestPoint);
let id;
if (hasMoreItems && nearestIndex === shownItemsCount - 1) {
id = 'more';
} else {
var _items$nearestIndex;
id = (_items$nearestIndex = items[nearestIndex]) === null || _items$nearestIndex === void 0 ? void 0 : _items$nearestIndex.id;
}
if (popupRef.current) {
if (id === 'more') {
popupRef.current.show();
} else {
popupRef.current.hide();
}
}
if (typeof onChange === 'function' && id) {
setPopupId(id);
setCurrentIndex(nearestIndex);
if (id !== 'more') onChange(id);
}
}
}, [animation, itemWidth, items, onChange, scope, setPopupId, shownItemsCount, snapPoints]);
return (0, _react2.useMemo)(() => /*#__PURE__*/_react2.default.createElement(_SliderButton.StyledSliderButton, {
$isDisabled: isDisabled,
ref: sliderButtonRef
}, /*#__PURE__*/_react2.default.createElement(_SliderButton.StyledSliderButtonButtonsWrapper, {
$isInvisible: true
}, pseudoButtons), /*#__PURE__*/_react2.default.createElement(_SliderButton.StyledMotionSliderButtonThumb, {
$isRounded: isRounded,
ref: scope,
drag: isDisabled ? false : 'x',
dragElastic: 0,
dragConstraints: isSliderBigger ? {
...dragRange,
right: dragRange.right - itemWidth
} : {
...dragRange
},
$width: itemWidth,
onDragEnd: handleDragEnd,
onDragStart: handleDragStart,
onClick: () => handleClick(currentId, currentIndex),
style: {
backgroundColor: thumbBackgroundColor
},
"data-slider-button-thumb": "true",
tabIndex: shouldEnableKeyboardHighlightingEffective && !isDisabled ? 0 : undefined,
role: shouldEnableKeyboardHighlightingEffective && !isDisabled ? 'button' : undefined,
onKeyDown: shouldEnableKeyboardHighlightingEffective && !isDisabled ? handleThumbKeyDown : undefined,
$shouldShowKeyboardHighlighting: shouldShowKeyboardHighlighting
}), /*#__PURE__*/_react2.default.createElement(_SliderButton.StyledSliderButtonWrapper, {
$isRounded: isRounded,
$isDisabled: isDisabled,
$width: !isSliderBigger ? dragRange.right + itemWidth : dragRange.right,
ref: sliderButtonWrapperRef,
style: {
backgroundColor
}
}, /*#__PURE__*/_react2.default.createElement(_react.AnimatePresence, null, /*#__PURE__*/_react2.default.createElement(_SliderButton.StyledSliderButtonButtonsWrapper, null, buttons)))), [backgroundColor, buttons, currentId, currentIndex, dragRange, handleClick, handleDragEnd, handleDragStart, handleThumbKeyDown, isDisabled, isRounded, isSliderBigger, itemWidth, pseudoButtons, scope, shouldEnableKeyboardHighlightingEffective, shouldShowKeyboardHighlighting, thumbBackgroundColor]);
};
SliderButton.displayName = 'SliderButton';
var _default = exports.default = SliderButton;
//# sourceMappingURL=SliderButton.js.map