| <link rel="import" href="../polymer/polymer.html"> |
| |
| <dom-module id="chops-carousel"> |
| <template> |
| <style> |
| :host { |
| width: 100%; |
| max-width: 100%; |
| box-sizing: border-box; |
| --chops-carousel-slide-height: 256px; |
| --chops-carousel-slide-width: 400px; |
| } |
| .carousel-content { |
| width: 100%; |
| max-width: 100%; |
| padding: 0.5em 32px; |
| overflow-x: hidden; |
| display: flex; |
| flex-direction: row; |
| align-items: center; |
| justify-content: center; |
| box-sizing: border-box; |
| } |
| .carousel-nav { |
| display: flex; |
| flex-direction: row; |
| justify-content: center; |
| width: 100%; |
| padding: 0.5em 16px; |
| box-sizing: border-box; |
| } |
| .carousel-nav-item { |
| border: 0; |
| border-radius: 50%; |
| width: 20px; |
| height: 20px; |
| margin: 0 8px; |
| background: hsl(227, 5%, 80%); |
| transition: box-shadow 0.5s ease-in-out, background 0.5s ease-in-out; |
| cursor: pointer; |
| } |
| .carousel-nav-item:hover, |
| .carousel-nav-item.focused { |
| background: hsl(227, 20%, 60%); |
| } |
| .carousel-nav-item.focused { |
| box-shadow: 0px 2px 8px -1px hsla(0, 0%, 0%, 0.3); |
| } |
| .img-wrapper { |
| width: var(--chops-carousel-slide-width); |
| height: var(--chops-carousel-slide-height); |
| overflow-y: hidden; |
| box-sizing: border-box; |
| transition: opacity 0.5s ease-in-out, box-shadow 0.5s linear; |
| opacity: 0.7; |
| cursor: pointer; |
| transform: scale(0.95, 0.95); |
| border: 1px solid hsl(0, 0%, 83%); |
| padding: 0.5em 8px; |
| margin: 0 8px; |
| background: hsl(0, 0%, 95%); |
| display: flex; |
| align-items: center; |
| justify-content: center; |
| flex-shrink: 0; |
| } |
| .img-wrapper.focused { |
| border: 1px solid hsl(227, 20%, 75%); |
| opacity: 1; |
| transform: scale(1, 1); |
| flex-grow: 1; |
| } |
| .img-wrapper:hover { |
| box-shadow: 0px 2px 8px -1px hsla(0, 0%, 0%, 0.5); |
| opacity: 1; |
| } |
| </style> |
| <div id="carouselRoot" class="carousel-content"> |
| <button id="prevImage" hidden$="[[!_showNav]]" |
| class="img-wrapper prev-image" |
| on-click="_focusPrevImage"> |
| <img src$="[[_prevImage]]" /> |
| </button> |
| <button |
| class="img-wrapper focused" |
| on-click="_focusedImageClicked"> |
| <img src$="[[_focusedImage]]" /> |
| </button> |
| <button id="nextImage" hidden$="[[!_showNav]]" |
| class="img-wrapper next-image" |
| on-click="_focusNextImage"> |
| <img src$="[[_nextImage]]" /> |
| </button> |
| </div> |
| <template is="dom-if" if="[[_showNav]]"> |
| <div id="carouselNav" class="carousel-nav"> |
| <template is="dom-repeat" items="[[images]]"> |
| <button |
| class$="carousel-nav-item [[_computeFocusedClass(focusedIndex, index)]]" |
| data-index$="[[index]]" |
| on-click="_handleNavClick"></button> |
| </template> |
| </div> |
| </template> |
| </template> |
| <script> |
| 'use strict'; |
| |
| /** |
| * `<chops-carousel>` |
| * |
| * This creates a carousel of images to browse through. |
| * |
| * @customElement |
| * @polymer |
| * @demo /demo/chops-carousel_demo.html |
| */ |
| class ChopsCarousel extends Polymer.Element { |
| static get is() { |
| return 'chops-carousel'; |
| } |
| |
| static get properties() { |
| return { |
| /** Index of the currently focused image. */ |
| focusedIndex: { |
| type: Number, |
| value: 0, |
| }, |
| /** |
| * Function handler that gets fired when the user clicks the |
| * focused image. Useful for functions like displaying the image in a |
| * lightbox. |
| */ |
| handleFocusedClick: Function, |
| /** Array of image urls to be displayed in the carousel. */ |
| images: { |
| type: Array, |
| value: [], |
| observer: '_preloadImages', |
| }, |
| _focusedImage: { |
| type: String, |
| computed: '_computeFocusedImage(images, focusedIndex)', |
| }, |
| _showNav: { |
| type: Boolean, |
| computed: '_computeShowNav(images.length)', |
| }, |
| _prevImage: { |
| type: String, |
| computed: '_computePrevImage(images, focusedIndex)', |
| }, |
| _nextImage: { |
| type: String, |
| computed: '_computeNextImage(images, focusedIndex)', |
| }, |
| }; |
| } |
| |
| _computeFocusedClass(focusedIndex, i) { |
| if (focusedIndex === i) { |
| return 'focused'; |
| } |
| return ''; |
| } |
| |
| _computeFocusedImage(images, i) { |
| if (!images || i >= images.length || i < 0) return; |
| return images[i]; |
| } |
| |
| _computeNextImage(images, i) { |
| if (!images || i >= images.length || i < 0) return; |
| return images[(i + 1) % images.length]; |
| } |
| |
| _computePrevImage(images, i) { |
| if (!images || i >= images.length || i < 0) return; |
| return images[(i - 1 + images.length) % images.length]; |
| } |
| |
| _computeShowNav(len) { |
| return len > 1; |
| } |
| |
| _focusedImageClicked(evt) { |
| if (this.handleFocusedClick) { |
| this.handleFocusedClick(); |
| } |
| } |
| |
| _focusNextImage() { |
| this.focusedIndex = (this.focusedIndex + 1) % this.images.length; |
| } |
| |
| _focusPrevImage() { |
| const l = this.images.length; |
| this.focusedIndex = (this.focusedIndex - 1 + l) % l; |
| } |
| |
| _handleNavClick(evt) { |
| this.focusedIndex = evt.target.dataset.index * 1; |
| } |
| |
| _preloadImages(images) { |
| images.forEach((img) => { |
| let image = new Image(); |
| image.src = img; |
| }); |
| } |
| } |
| customElements.define(ChopsCarousel.is, ChopsCarousel); |
| </script> |
| <dom-module> |