Source code for "How to write JavaScript components with jQuery"
import $ from 'jquery' | |
import './styles.scss' | |
import CarouselControls from '../CarouselControls' | |
import CarouselDots from '../CarouselDots' | |
import CarouselSlider from '../CarouselSlider' | |
import mod from '../../lib/mod' | |
class CarouselBlock { | |
constructor({root}) { | |
this.root = $(root) | |
this.slides = new CarouselSlider({ | |
root: this.root.children('.carousel-slider')[0] | |
}) | |
this.controls = new CarouselControls({ | |
root: this.root.children('.carousel-controls')[0] | |
}) | |
this.dots = new CarouselDots({ | |
root: this.root.children('.carousel-dots')[0] | |
}) | |
this.currentIndex = 0 | |
this.controls.on('clicknext', () => this.nextSlide()) | |
this.controls.on('clickprev', () => this.prevSlide()) | |
this.dots.on('clickdot', index => this.slideTo(index)) | |
} | |
slideTo(index) { | |
this.slides.slideTo(index) | |
this.dots.highlightDot(index) | |
this.currentIndex = index | |
} | |
nextSlide() { | |
let nextIndex = mod(this.currentIndex + 1, this.slides.slideCount) | |
this.slideTo(nextIndex) | |
} | |
prevSlide() { | |
let prevIndex = mod(this.currentIndex - 1, this.slides.slideCount) | |
this.slideTo(prevIndex) | |
} | |
} | |
export default CarouselBlock |
@import "vertical-rhythm"; | |
.carousel-block { | |
margin: vr(5) auto; | |
position: relative; | |
width: 768px; | |
.carousel-slider { | |
margin-bottom: vr(1); | |
} | |
.carousel-controls { | |
position: absolute; | |
top: 50%; | |
transform: translateY(-50%); | |
width: 100%; | |
} | |
} |
import $ from 'jquery' | |
import EventEmitter from 'eventemitter3' | |
import './styles.scss' | |
class CarouselControls extends EventEmitter { | |
constructor({root}) { | |
super() | |
this.root = $(root) | |
this.buttons = this.root.children('.carousel-controls__button') | |
this.buttons.on('click', ev => this._handleClick(ev)) | |
} | |
_handleClick(ev) { | |
ev.preventDefault() | |
const target = $(ev.target) | |
if (target.hasClass('carousel-controls__next')) { | |
this.emit('clicknext') | |
} else { | |
this.emit('clickprev') | |
} | |
} | |
} | |
export default CarouselControls |
@import "button-reset"; | |
@import "vertical-rhythm"; | |
.carousel-controls { | |
display: flex; | |
flex-direction: row; | |
justify-content: space-between; | |
&__button { | |
@extend %button-reset; | |
background-color: rgba(#000, 0.1); | |
color: #fff; | |
height: vr(2); | |
width: vr(3); | |
} | |
} |
import $ from 'jquery' | |
import EventEmitter from 'eventemitter3' | |
import './styles.scss' | |
class CarouselDots extends EventEmitter { | |
constructor({root}) { | |
super() | |
this.root = $(root) | |
this.dots = this.root.children('.carousel-dots__dot') | |
this.dots.on('click', ev => this._handleClick(ev)) | |
} | |
_handleClick(ev) { | |
ev.preventDefault() | |
const index = this.dots.index(ev.target) | |
this.highlightDot(index) | |
this.emit('clickdot', index) | |
} | |
highlightDot(index) { | |
this.dots.removeClass('carousel-dots__dot--active') | |
this.dots.eq(index).addClass('carousel-dots__dot--active') | |
} | |
} | |
export default CarouselDots |
@import "button-reset"; | |
@import "vertical-rhythm"; | |
.carousel-dots { | |
display: flex; | |
flex-direction: row; | |
justify-content: center; | |
&__dot { | |
@extend %button-reset; | |
background-color: rgba(#000, 0.2); | |
border-radius: 50%; | |
height: vr(0.5); | |
margin: 0 vr(0.5); | |
transition: background-color 0.4s ease; | |
width: vr(0.5); | |
&--active { | |
background-color: rgba(#000, 0.5); | |
} | |
} | |
} |
import $ from 'jquery' | |
import './styles.scss' | |
class CarouselSlider { | |
constructor({root}) { | |
this.root = $(root) | |
this.slideList = this.root.children('.carousel-slider__slide-list') | |
this.slides = this.slideList.children('.carousel-slider__slide') | |
} | |
get slideCount() { | |
return this.slides.length | |
} | |
slideTo(index) { | |
this.slideList.css({transform: `translateX(-${100 * index}%)`}); | |
} | |
} | |
export default CarouselSlider |
.carousel-slider { | |
overflow: hidden; | |
&__slide-list { | |
display: flex; | |
flex-direction: row; | |
list-style-type: none; | |
margin: 0; | |
padding: 0; | |
transform: translateX(0); | |
transition: transform 0.4s ease; | |
} | |
&__slide { | |
flex: 1 0 100%; | |
img { | |
display: block; | |
max-width: 100%; | |
} | |
} | |
} |
// Modulo that works with negative numbers | |
export default function mod(n, m) { | |
return ((n % m) + m) % m; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment