diff --git a/package-lock.json b/package-lock.json index 6cc1f07..bb69b24 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,7 +8,6 @@ "version": "7.5.5", "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.5.5.tgz", "integrity": "sha512-28QvEGyQyNkB0/m2B4FU7IEZGK2NUrcMtT6BZEFALTguLk+AUT6ofsHtPk5QyjAdUkpMJ+/Em+quwz4HOt30AQ==", - "dev": true, "requires": { "regenerator-runtime": "^0.13.2" }, @@ -16,8 +15,7 @@ "regenerator-runtime": { "version": "0.13.3", "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.3.tgz", - "integrity": "sha512-naKIZz2GQ8JWh///G7L3X6LaQUAMp2lvb1rvwwsURe/VXwD6VMfr+/1NuNw3ag8v2kY1aQ/go5SNn79O9JU7yw==", - "dev": true + "integrity": "sha512-naKIZz2GQ8JWh///G7L3X6LaQUAMp2lvb1rvwwsURe/VXwD6VMfr+/1NuNw3ag8v2kY1aQ/go5SNn79O9JU7yw==" } } }, @@ -6538,8 +6536,7 @@ "keycode": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/keycode/-/keycode-2.2.0.tgz", - "integrity": "sha1-PQr1bce4uOXLqNCpfxByBO7CKwQ=", - "dev": true + "integrity": "sha1-PQr1bce4uOXLqNCpfxByBO7CKwQ=" }, "killable": { "version": "1.0.1", @@ -8905,25 +8902,13 @@ "dev": true }, "react-event-listener": { - "version": "0.6.4", - "resolved": "https://registry.npmjs.org/react-event-listener/-/react-event-listener-0.6.4.tgz", - "integrity": "sha512-t7VSjIuUFmN+GeyKb+wm025YLeojVB85kJL6sSs0wEBJddfmKBEQz+CNBZ2zBLKVWkPy/fZXM6U5yvojjYBVYQ==", - "dev": true, + "version": "0.6.6", + "resolved": "https://registry.npmjs.org/react-event-listener/-/react-event-listener-0.6.6.tgz", + "integrity": "sha512-+hCNqfy7o9wvO6UgjqFmBzARJS7qrNoda0VqzvOuioEpoEXKutiKuv92dSz6kP7rYLmyHPyYNLesi5t/aH1gfw==", "requires": { - "@babel/runtime": "7.0.0", + "@babel/runtime": "^7.2.0", "prop-types": "^15.6.0", "warning": "^4.0.1" - }, - "dependencies": { - "@babel/runtime": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.0.0.tgz", - "integrity": "sha512-7hGhzlcmg01CvH1EHdSPVXYX1aJ8KCEyz6I9xYIi/asDtzBPMyMhVibhM/K6g/5qnKBwjZtp10bNZIEFTRW1MA==", - "dev": true, - "requires": { - "regenerator-runtime": "^0.12.0" - } - } } }, "react-group": { @@ -11299,7 +11284,6 @@ "version": "4.0.2", "resolved": "https://registry.npmjs.org/warning/-/warning-4.0.2.tgz", "integrity": "sha512-wbTp09q/9C+jJn4KKJfJfoS6VleK/Dti0yqWSm6KMvJ4MRCXFQNapHuJXutJIrWV0Cf4AhTdeIe4qdKHR1+Hug==", - "dev": true, "requires": { "loose-envify": "^1.0.0" } diff --git a/package.json b/package.json index c7e4ba3..efc0719 100644 --- a/package.json +++ b/package.json @@ -55,8 +55,10 @@ }, "dependencies": { "classnames": "^2.2.5", + "keycode": "^2.2.0", "material-ui-dots": "^2.0.0", - "prop-types": "^15.5.8" + "prop-types": "^15.5.8", + "react-event-listener": "^0.6.6" }, "standard": { "parser": "babel-eslint" diff --git a/src/AutoRotatingCarousel.js b/src/AutoRotatingCarousel.js index 45ea7c3..4ec7b66 100644 --- a/src/AutoRotatingCarousel.js +++ b/src/AutoRotatingCarousel.js @@ -14,7 +14,7 @@ import Backdrop from '@material-ui/core/Backdrop' import Dots from 'material-ui-dots' import classNames from 'classnames' import Carousel from './SwipableCarouselView' -import { modulo } from './util' +import { bounded, modulo } from './util' const styles = { root: { @@ -51,6 +51,10 @@ const styles = { position: 'absolute', top: 'calc((100% - 96px) / 2 + 24px)' }, + arrowDisabled: { + opacity: 0.5, + cursor: 'default' + }, arrowLeft: { left: -96 }, @@ -116,21 +120,22 @@ class AutoRotatingCarousel extends Component { handleContentClick = (e) => e.stopPropagation() || e.preventDefault() - handleChange = (slideIndex) => { + handleChange = (index) => { + const slideIndex = this.getIndex(index, this.slideCount) this.setState({ slideIndex }, this.onChange(slideIndex)) } decreaseIndex () { - const slideIndex = this.state.slideIndex - 1 + const slideIndex = this.getIndex(this.state.slideIndex - 1, this.slideCount) this.setState({ slideIndex }, this.onChange(slideIndex)) } increaseIndex () { - const slideIndex = this.state.slideIndex + 1 + const slideIndex = this.getIndex(this.state.slideIndex + 1, this.slideCount) this.setState({ slideIndex }, this.onChange(slideIndex)) @@ -138,15 +143,26 @@ class AutoRotatingCarousel extends Component { onChange (slideIndex) { if (this.props.onChange) { - this.props.onChange(modulo(slideIndex, this.props.children.length)) + this.props.onChange(this.getIndex(slideIndex, this.slideCount)) } } + getIndex (idx, n) { + const { circular } = this.props + const indexer = circular ? modulo : bounded + return indexer(idx, n) + } + + get slideCount () { + return this.props.children.length + } + render () { const { autoplay, ButtonProps, children, + circular, classes, containerStyle, hideArrows, @@ -161,7 +177,10 @@ class AutoRotatingCarousel extends Component { } = this.props const landscape = mobile && landscapeProp const transitionDuration = { enter: duration.enteringScreen, exit: duration.leavingScreen } - const hasMultipleChildren = children.length != null + const hasMultipleChildren = this.slideCount != null + + const disableLeftArrow = !circular && this.state.slideIndex === 0 + const disableRightArrow = !circular && this.state.slideIndex === this.slideCount - 1 const carousel = ( this.decreaseIndex()} + disableRipple={disableLeftArrow} > this.increaseIndex()} + disableRipple={disableRightArrow} > @@ -265,7 +286,8 @@ AutoRotatingCarousel.defaultProps = { interval: 3000, mobile: false, open: false, - hideArrows: false + hideArrows: false, + circular: true } AutoRotatingCarousel.propTypes = { @@ -296,7 +318,9 @@ AutoRotatingCarousel.propTypes = { /** Controls whether the AutoRotatingCarousel is opened or not. */ open: PropTypes.bool, /** If `true`, the left and right arrows are hidden in the desktop version. */ - hideArrows: PropTypes.bool + hideArrows: PropTypes.bool, + /** If `false`, the carousel becomes non-circular and its buttons are disabled in the boundaries. */ + circular: PropTypes.bool } export default withStyles(styles)(AutoRotatingCarousel) diff --git a/src/AutoRotatingCarousel.md b/src/AutoRotatingCarousel.md index 17d98bc..262cc5c 100644 --- a/src/AutoRotatingCarousel.md +++ b/src/AutoRotatingCarousel.md @@ -102,3 +102,38 @@ const Button = require('@material-ui/core/Button').default; ``` + +### Desktop mode (non circular) + +``` +const Slide = require('./Slide').default; +const { red, blue, green } = require('@material-ui/core/colors'); +const Button = require('@material-ui/core/Button').default; + +
+ + setState({ open: false })} + onStart={() => setState({ open: false })} + style={{ position: 'absolute' }} + circular={false} + > + } + mediaBackgroundStyle={{ backgroundColor: red[400] }} + style={{ backgroundColor: red[600] }} + title='This is a very cool feature' + subtitle='Just using this will blow your mind.' + /> + } + mediaBackgroundStyle={{ backgroundColor: blue[400] }} + style={{ backgroundColor: blue[600] }} + title='Ever wanted to be popular?' + subtitle='Well just mix two colors and your are good to go!' + /> + +
+``` \ No newline at end of file diff --git a/src/SwipableCarouselView.js b/src/SwipableCarouselView.js index bb53a13..a9c45d6 100644 --- a/src/SwipableCarouselView.js +++ b/src/SwipableCarouselView.js @@ -1,18 +1,46 @@ -import React from 'react' +import React, { Component } from 'react' import autoPlay from 'react-swipeable-views-utils/lib/autoPlay' import virtualize from 'react-swipeable-views-utils/lib/virtualize' import bindKeyboard from 'react-swipeable-views-utils/lib/bindKeyboard' import SwipeableViews from 'react-swipeable-views' +import keycode from 'keycode'; +import EventListener from 'react-event-listener'; import { modulo } from './util' +const NonVirtualizedSwipeViews = bindKeyboard(SwipeableViews) const VirtualizeSwipeViews = bindKeyboard(virtualize(SwipeableViews)) const VirtualizeAutoPlaySwipeViews = autoPlay(VirtualizeSwipeViews) const carouselSlideRenderer = (children) => ({index, key}) => React.cloneElement(children[modulo(index, children.length)], {key}) -export default function Carousel ({children, autoplay, ...other}) { +class NonCircularCarousel extends Component { + handleKeyDown = event => { + const { index, children } = this.props + switch (keycode(event)) { + case 'left': + if (index === 0) event.stopPropagation() + break + case 'right': + if (index === children.length - 1) event.stopPropagation() + break + } + } + + render () { + return ( + + + + ) + } +} + +export default function Carousel ({children, autoplay, circular, ...other}) { const slideRenderer = carouselSlideRenderer(children) + if (!circular) { + return + } return autoplay ? ( ((a % n) + n) % n + +export const bounded = (idx, n) => Math.max(0, Math.min(idx, n - 1))