UNPKG

@exabytellc/utils

Version:

EB react utils to make everything a little easier!

230 lines (212 loc) 7.84 kB
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, {}) }); };