@exabytellc/utils
Version:
EB react utils to make everything a little easier!
230 lines (212 loc) • 7.84 kB
JavaScript
import { Children, createElement as _createElement } from "react";
import { Slider as SlickSlider } from "react-slick";
import { BiChevronLeft, BiChevronRight } from "react-icons/bi";
import { TranslationBloc } from "../utils/blocs/TranslationBloc";
/**
* Carousel
* - Accepts any react-slick option via ...rest
* - Supports MUI-style breakpoint objects for some props:
* e.g. slidesToShow={ { xs: 1, md: 3 } } or slidesToShow={1}
* - Auto-parses those breakpoint objects into react-slick's responsive array.
*/
import { jsx as _jsx } from "react/jsx-runtime";
export default function Slider(_ref) {
let {
className,
// common defaults
dots = true,
infinite = true,
speed = 500,
slidesToShow = 1,
slidesToScroll = 1,
autoplay = true,
autoplaySpeed = 5000,
arrows = true,
arrowsOutside = false,
arrowsSolid = false,
centerMode = false,
centerPadding = 20,
itemProps,
children,
// allow user to directly pass a react-slick "responsive" to merge with computed one
responsive: userResponsive,
...rest
} = _ref;
const {
dir
} = TranslationBloc();
// keys that we will auto-parse when provided as breakpoint objects
const parseableKeys = ["slidesToShow", "slidesToScroll", "centerMode", "arrows", "autoplay", "autoplaySpeed", "centerPadding"];
const {
baseSettings,
computedResponsive
} = buildResponsiveSettings({
slidesToShow,
slidesToScroll,
centerMode,
arrows,
autoplay,
autoplaySpeed,
centerPadding,
...rest
}, parseableKeys);
// Merge user-provided responsive (if any). User responsive takes precedence for exact breakpoints,
// but we append both (react-slick will pick whichever matches).
const finalResponsive = mergeResponsiveArrays(computedResponsive, userResponsive);
console.log(finalResponsive);
return /*#__PURE__*/_jsx(SlickSlider, {
dots: dots,
infinite: infinite,
speed: speed
// base settings (non-responsive / large-screen)
,
slidesToShow: baseSettings.slidesToShow,
slidesToScroll: baseSettings.slidesToScroll,
autoplay: baseSettings.autoplay,
autoplaySpeed: baseSettings.autoplaySpeed,
arrows: baseSettings.arrows ?? arrows,
prevArrow: /*#__PURE__*/_jsx(PrevArrow, {
outside: arrowsOutside,
solid: arrowsSolid
}),
nextArrow: /*#__PURE__*/_jsx(NextArrow, {
outside: arrowsOutside,
solid: arrowsSolid
}),
className: className,
waitForAnimate: false,
centerMode: baseSettings.centerMode ?? centerMode,
centerPadding: baseSettings.centerPadding ?? centerPadding,
swipeToSlide: true,
rtl: dir === "rtl",
responsive: finalResponsive,
...rest,
children: Children.toArray(children).map((child, index) => /*#__PURE__*/_createElement("div", {
...(itemProps ?? {}),
key: child?.key ?? index,
dir: dir
}, child))
});
}
/* ---------- helpers ---------- */
/**
* MUI-style breakpoints (min-width)
* We'll treat incoming objects as "value applies from this min-width upwards".
* We translate them into react-slick's "responsive" (which uses max-width).
*/
const BREAKPOINTS = {
xs: 0,
sm: 600,
md: 900,
lg: 1200,
xl: 1536,
"2xl": 1920
};
const BP_ORDER = ["xs", "sm", "md", "lg", "xl", "2xl"];
/**
* Build base settings and a react-slick 'responsive' array for the parsed keys.
*
* Logic:
* - For each parseable key:
* - if value is primitive -> treat as base setting.
* - if value is an object { xs: v1, md: v2, ... } (min-width semantics):
* * base setting := value at the largest defined breakpoint
* * for each smaller defined breakpoint, emit a responsive entry that applies for viewports
* smaller than the next larger defined breakpoint (i.e. max-width = nextPx - 1)
* * aggregate settings across keys for each breakpoint number
*/
function buildResponsiveSettings(allOptions, keysToParse) {
const baseSettings = {};
// map breakpointMax -> settings
const breakpointMap = new Map();
// helper: is plain object
const isPlainObject = v => v && typeof v === "object" && !Array.isArray(v);
// For each key, produce base value and a list of smaller-range responsive entries
keysToParse.forEach(key => {
const val = allOptions[key];
if (val === undefined) return;
if (!isPlainObject(val)) {
baseSettings[key] = val;
return;
}
// val is an object of min-width semantics
// collect defined breakpoints present for this key
const defined = BP_ORDER.map(bp => ({
bp,
px: BREAKPOINTS[bp],
value: val[bp]
})).filter(x => x.value !== undefined).sort((a, b) => a.px - b.px); // ascending by px
if (defined.length === 0) return;
// base (largest breakpoint value)
const largest = defined[defined.length - 1];
baseSettings[key] = largest.value;
// for each smaller-defined breakpoint, create responsive entry that applies for max-width = nextPx - 1
// e.g. defined: [ {xs:3, px:0}, {md:1, px:900} ]
// -> responsive entry at breakpoint = md.px - 1 (899) with setting { key: xs.value }
for (let i = 0; i < defined.length - 1; i++) {
const current = defined[i]; // smaller
const next = defined[i + 1]; // next larger
const breakpointMax = next.px; // max-width in px
if (breakpointMax <= 0) continue;
const entry = breakpointMap.get(breakpointMax) ?? {};
entry[key] = current.value;
breakpointMap.set(breakpointMax, entry);
}
// Also handle the case where there is a defined smallest bp but nothing larger:
// (already covered because largest used as base; nothing to do)
});
// Convert breakpointMap to react-slick responsive array
const computedResponsive = Array.from(breakpointMap.entries()).map(_ref2 => {
let [breakpoint, settings] = _ref2;
return {
breakpoint: Number(breakpoint),
settings
};
}).sort((a, b) => b.breakpoint - a.breakpoint); // largest breakpoint first (recommended)
return {
baseSettings,
computedResponsive
};
}
/**
* Merge two responsive arrays (computed + user). Returns combined array.
* We will simply concat and sort by breakpoint desc. User entries kept too.
*/
function mergeResponsiveArrays(computed, user) {
if (!computed) computed = [];
if (!user) user = [];
// If user passed an object (single entry), normalize to array
const normalize = r => (Array.isArray(r) ? r : [r]).filter(Boolean);
const merged = [...normalize(computed), ...normalize(user)];
return merged.filter(Boolean).map(e => ({
...e,
breakpoint: Number(e.breakpoint)
})).sort((a, b) => b.breakpoint - a.breakpoint);
}
/* ---------- custom arrows ---------- */
const PrevArrow = _ref3 => {
let {
onClick,
outside = false,
solid = false
} = _ref3;
return /*#__PURE__*/_jsx("button", {
className: `absolute text-5xl ${outside ? "-left-10" : "left-2"} top-1/2 transform -translate-y-1/2 ${solid ? "bg-transparent bg-primary hover:bg-white text-white hover:text-primary rounded-full" : " text-white hover:bg-primary rounded-full"} transition duration-300 z-10`,
onClick: onClick,
"aria-label": "Previous",
children: /*#__PURE__*/_jsx(BiChevronLeft, {})
});
};
const NextArrow = _ref4 => {
let {
onClick,
outside = false,
solid = false
} = _ref4;
return /*#__PURE__*/_jsx("button", {
className: `absolute text-5xl ${outside ? "-right-10" : "right-2"} top-1/2 transform -translate-y-1/2 ${solid ? "bg-transparent bg-primary hover:bg-white text-white hover:text-primary rounded-full" : " text-white hover:bg-primary rounded-full"} transition duration-300 z-10`,
onClick: onClick,
"aria-label": "Next",
children: /*#__PURE__*/_jsx(BiChevronRight, {})
});
};