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; |
|
} |