UNPKG

@cimpress/react-components

Version:
222 lines (219 loc) 9.86 kB
var __rest = (this && this.__rest) || function (s, e) { var t = {}; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0) t[p] = s[p]; if (s != null && typeof Object.getOwnPropertySymbols === "function") for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) { if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i])) t[p[i]] = s[p[i]]; } return t; }; import React, { useEffect, useState } from 'react'; import { cx, css } from '@emotion/css'; import { horizon } from '../colors'; import CarouselSlide from './CarouselSlide'; import CarouselControl from './CarouselControl'; import CarouselIndicator from './CarouselIndicator'; import CarouselLoading from './CarouselLoading'; import CarouselFallback from './CarouselFallback'; import cvar from '../theme/cvar'; const THUMBNAIL_WIDTH = 70; const getCarouselWidthForThumbs = (numberOfThumbs) => 40 + THUMBNAIL_WIDTH * numberOfThumbs; const sizeMap = { xs: { maxThumbs: 3, pxWidth: getCarouselWidthForThumbs(3) }, s: { maxThumbs: 4, pxWidth: getCarouselWidthForThumbs(4) }, m: { maxThumbs: 5, pxWidth: getCarouselWidthForThumbs(5) }, l: { maxThumbs: 6, pxWidth: getCarouselWidthForThumbs(6) }, xl: { maxThumbs: 7, pxWidth: getCarouselWidthForThumbs(7) }, }; const carouselCss = css ` position: relative; div { height: 100%; } `; const slideCss = css ` border: 1px solid ${cvar('color-border-light')}; background-color: ${cvar('color-background')}; height: 100%; `; const carouselInnerCss = css ` position: relative; width: 100%; overflow: hidden; img { margin: auto; max-height: 100%; width: auto; height: auto; position: absolute; top: 0; bottom: 0; left: 0; right: 0; display: block; max-width: 100%; line-height: 1; } `; const carouselNavigationCss = css ` margin-top: 25px; display: -ms-flexbox; display: flex; -ms-flex-align: center; align-items: center; -ms-flex-pack: justify; justify-content: space-between; `; const carouselNavigationSmallCss = css ` margin-top: 16px; `; const carouselNavigationVerticalCss = css ` margin-top: 0; margin-right: 25px; -ms-flex-direction: column; flex-direction: column; -ms-flex-pack: start; justify-content: flex-start; `; const carouselIndicatorsCss = css ` position: relative; bottom: 0; left: 0; margin: 0; width: auto; line-height: 0; overflow: hidden; height: 70px; display: -ms-flexbox; display: flex; -ms-flex-wrap: nowrap; flex-wrap: nowrap; z-index: 15; padding-left: 0; text-align: center; list-style: none; flex: 1; li { transition: 0.6s ease-in-out all; -ms-flex-negative: 0; flex-shrink: 0; margin: 5px; width: 60px; height: 60px; background-color: ${cvar('color-background')}; border-radius: 0; overflow: hidden; text-indent: 0; position: relative; display: inline-block; cursor: pointer; img { margin: auto; max-height: 100%; max-width: 100%; width: auto; height: auto; position: absolute; top: 0; bottom: 0; left: 0; right: 0; } } `; const smallCss = css ` font-size: 85%; width: 100%; `; export const Carousel = (_a) => { var { loading, size = 'm', pxSize = 200, minimal = false, children, onCarouselClick, fallbackPreview = React.createElement(CarouselFallback, null), hideIndicators = false, showZoomOverlay = false, zoomOverlayContent, className, style, landscape = false, currentSlide = 0, onSlideChanged } = _a, rest = __rest(_a, ["loading", "size", "pxSize", "minimal", "children", "onCarouselClick", "fallbackPreview", "hideIndicators", "showZoomOverlay", "zoomOverlayContent", "className", "style", "landscape", "currentSlide", "onSlideChanged"]); const total = React.Children.count(children); const pixels = size && !minimal ? sizeMap[size].pxWidth : pxSize; const allowNavigation = (minimal && total > 1) || (!minimal && total > sizeMap[size].maxThumbs); const isLandscape = minimal ? false : landscape; /** * State and control for slides */ const [slideState, setSlideState] = useState({ currentSlide: currentSlide > total - 1 ? 0 : currentSlide, previousSlide: null, transitioning: false, timer: null, direction: '', }); useEffect(() => { setSlideState(previousState => { if (currentSlide !== undefined && previousState.currentSlide !== currentSlide) { const slideNumber = currentSlide < 0 || currentSlide >= total ? previousState.currentSlide : currentSlide; return Object.assign(Object.assign({}, previousState), { currentSlide: slideNumber }); } return previousState; }); }, [currentSlide, total]); const clearPendingAnimation = () => { if (slideState.timer) { clearTimeout(slideState.timer); } }; const endAnimation = () => window.setTimeout(() => setSlideState(prevState => (Object.assign(Object.assign({}, prevState), { previousSlide: null, transitioning: false }))), 600); const goToSlide = (nextSlide, direction) => { if (nextSlide === slideState.currentSlide) { return; } clearPendingAnimation(); setSlideState(prevState => (Object.assign(Object.assign({}, prevState), { direction: direction || (nextSlide > slideState.currentSlide ? 'next' : 'prev'), previousSlide: prevState.currentSlide, currentSlide: nextSlide, transitioning: true, timer: endAnimation() }))); onSlideChanged && onSlideChanged(nextSlide); }; const goToNextSlide = () => { let nextSlide = slideState.currentSlide + 1; if (nextSlide >= React.Children.count(children)) { nextSlide = 0; } goToSlide(nextSlide, 'next'); }; const goToPreviousSlide = () => { const nextSlide = slideState.currentSlide > 0 ? slideState.currentSlide - 1 : React.Children.count(children) - 1; goToSlide(nextSlide, 'prev'); }; /** * State and controls for thumbnails */ const [thumbsPosition, setThumbsPosition] = useState(0); const goToThumbs = (direction) => { let newThumbsPosition = thumbsPosition; const totalLength = React.Children.count(children) * THUMBNAIL_WIDTH; const thumbsWidth = sizeMap[size].maxThumbs * THUMBNAIL_WIDTH; const loopPosition = direction === 'next' ? 0 : (Math.floor(totalLength / thumbsWidth) - 1) * thumbsWidth * -1; direction === 'next' ? (newThumbsPosition -= thumbsWidth) : (newThumbsPosition += thumbsWidth); newThumbsPosition = Math.abs(newThumbsPosition) >= totalLength || newThumbsPosition > 0 ? loopPosition : newThumbsPosition; setThumbsPosition(newThumbsPosition); }; const goToNextThumbs = () => goToThumbs('next'); const goToPreviousThumbs = () => goToThumbs('prev'); const slides = React.Children.map(children, (child, i) => (React.createElement(CarouselSlide, { active: i === slideState.currentSlide, index: i, animateOut: i === slideState.previousSlide && slideState.transitioning, animateIn: i === slideState.currentSlide && slideState.previousSlide !== null && slideState.transitioning, direction: slideState.direction, onCarouselClick: onCarouselClick, showZoomOverlay: showZoomOverlay, zoomOverlayContent: zoomOverlayContent }, child))); const componentStyle = isLandscape ? { height: `${pixels}px`, display: 'flex', flexDirection: 'row-reverse', width: 'fit-content' } : { width: `${pixels}px` }; return (React.createElement("div", Object.assign({ className: cx('crc-carousel', className), style: Object.assign(Object.assign({}, componentStyle), style) }, rest), React.createElement("div", { style: isLandscape ? { width: `${pixels}px` } : { height: `${pixels}px` } }, React.createElement("div", { className: cx(carouselCss, slideCss) }, React.createElement("div", { className: carouselInnerCss, role: "listbox" }, loading ? React.createElement(CarouselLoading, { size: pixels }) : slides && slides.length ? slides : fallbackPreview))), !hideIndicators && (React.createElement("div", { className: cx(carouselNavigationCss, { [carouselNavigationSmallCss]: minimal, [carouselNavigationVerticalCss]: isLandscape, }) }, React.createElement(CarouselControl, { disabled: !allowNavigation, onClick: minimal ? goToPreviousSlide : goToPreviousThumbs, large: !minimal, direction: isLandscape ? 'up' : 'left', hintText: "Previous" }), React.createElement("ol", { className: cx(carouselIndicatorsCss, { [css ` flex-direction: column; `]: isLandscape, [css ` justify-content: center; `]: !total, }), style: minimal ? { height: 'auto', overflow: 'visible' } : {} }, total && !minimal ? (React.Children.map(children, (child, i) => (React.createElement(CarouselIndicator, { slideNumber: i, active: slideState.currentSlide === i, goToSlide: goToSlide, offset: isLandscape ? { top: thumbsPosition } : { left: thumbsPosition } }, loading ? null : child)))) : minimal ? (React.createElement("div", { className: smallCss }, total > 0 ? `${slideState.currentSlide + 1} / ${total}` : '– / –')) : (React.createElement("li", { style: { backgroundColor: horizon } }))), React.createElement(CarouselControl, { disabled: !allowNavigation, onClick: minimal ? goToNextSlide : goToNextThumbs, large: !minimal, direction: isLandscape ? 'down' : 'right', hintText: "Next" }))))); }; //# sourceMappingURL=Carousel.js.map