@cimpress/react-components
Version:
React components to support the MCP styleguide
222 lines (219 loc) • 9.86 kB
JavaScript
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