Skip to content

Commit

Permalink
#97 feat(props): add adjustableHeight prop (#292)
Browse files Browse the repository at this point in the history
* #97 feat(props): add `adjustableHeight` prop

Allow the height of the carousel to change based on its current content.

* fix(example): Set fixed width slide content

* fix(perf): only watch additional mutation events for adjustableHeight

* fix(lint): Missing semi-colon

Fix listing issue

* fix(bug): adjustableHeight - Incorrect initial height

The initial `currentHeight` prop of the carousel was not calculating correctly because the CSS was constraining the width of the `slide`. This is only noticeable when slide content is slow to load. The additional CSS ensures the slide width is correct when calculating the initial carousel height.

* fix(bug): Add missing return statement
  • Loading branch information
adam-26 authored and quinnlangille committed Oct 19, 2018
1 parent 6823411 commit f1e6314
Show file tree
Hide file tree
Showing 7 changed files with 214 additions and 62 deletions.
21 changes: 21 additions & 0 deletions play/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -345,3 +345,24 @@ play("Carousel", module)

}
})
.add("With adjustableHeight", {
template:
`<div style="width: 100%; display: flex; justify-content: center; margin-top: 40px;">
<carousel style="width: 300px;" :adjustableHeight="true" :navigationEnabled="true" :perPage="1">
<slide v-for="(slide, idx) in slides">
<div style="width: 300px;">
<img :style="'height: ' + ((idx + 1) * 50) + 'px;'" :src="slide" />
</div>
</slide>
</carousel>
</div>`,
components: {
Carousel,
Slide
},
data() {
return {
slides: images
}
}
})
82 changes: 79 additions & 3 deletions src/Carousel.vue
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
'webkit-flex-basis': `${slideWidth}px`,
'flex-basis': `${slideWidth}px`,
'visibility': slideWidth ? 'visible' : 'hidden',
'height': `${currentHeight}`,
'padding-left': `${padding}px`,
'padding-right': `${padding}px`
}"
Expand Down Expand Up @@ -99,7 +100,8 @@ export default {
refreshRate: 16,
slideCount: 0,
transitionstart: "transitionstart",
transitionend: "transitionend"
transitionend: "transitionend",
currentHeight: "auto"
};
},
mixins: [autoplay],
Expand Down Expand Up @@ -281,6 +283,20 @@ export default {
centerMode: {
type: Boolean,
default: false
},
/**
* Adjust the height of the carousel to the current slide(s)
*/
adjustableHeight: {
type: Boolean,
default: false
},
/**
* Slide transition easing for adjustableHeight
* Any valid CSS transition easing accepted
*/
adjustableHeightEasing: {
type: String
}
},
Expand Down Expand Up @@ -429,7 +445,14 @@ export default {
return this.centerMode && !this.isNavigationRequired ? true : false;
},
transitionStyle() {
return `${this.speed / 1000}s ${this.easing} transform`;
const speed = `${this.speed / 1000}s`;
const transtion = `${speed} ${this.easing} transform`;
if (this.adjustableHeight) {
return `${transtion}, height ${speed} ${this.adjustableHeightEasing ||
this.easing}`;
}
return transtion;
},
padding() {
const padding = this.spacePadding;
Expand Down Expand Up @@ -480,10 +503,22 @@ export default {
window.MozMutationObserver;
if (MutationObserver) {
const config = { attributes: true, data: true };
let config = {
attributes: true,
data: true
};
if (this.adjustableHeight) {
config = {
...config,
childList: true,
subtree: true,
characterData: true
};
}
this.mutationObserver = new MutationObserver(() => {
this.$nextTick(() => {
this.computeCarouselWidth();
this.computeCarouselHeight();
});
});
if (this.$parent.$el) {
Expand Down Expand Up @@ -530,6 +565,29 @@ export default {
}
return this.carouselWidth;
},
/**
* Get the maximum height of the carousel active slides
* @return {String} The carousel height
*/
getCarouselHeight() {
if (!this.adjustableHeight) {
return "auto";
}
const slideOffset = this.currentPerPage * (this.currentPage + 1) - 1;
const maxSlideHeight = [...Array(this.currentPerPage)]
.map((_, idx) => this.getSlide(slideOffset + idx))
.reduce(
(clientHeight, slide) =>
Math.max(clientHeight, (slide && slide.$el.clientHeight) || 0),
0
);
this.currentHeight =
maxSlideHeight === 0 ? "auto" : `${maxSlideHeight}px`;
return this.currentHeight;
},
/**
* Filter slot contents to slide instances and return length
* @return {Number} The number of slides
Expand All @@ -543,6 +601,16 @@ export default {
).length) ||
0;
},
/**
* Gets the slide at the specified index
* @return {Object} The slide at the specified index
*/
getSlide(index) {
const slides = this.$children.filter(
child => child.$vnode.tag.indexOf("slide") > -1
);
return slides[index];
},
/**
* Set the current page to a specific value
* This function will only apply the change if the value is within the carousel bounds
Expand Down Expand Up @@ -663,6 +731,7 @@ export default {
},
onResize() {
this.computeCarouselWidth();
this.computeCarouselHeight();
this.dragging = true; // force a dragging to disable animation
this.render();
Expand Down Expand Up @@ -702,6 +771,12 @@ export default {
this.getCarouselWidth();
this.setCurrentPageInBounds();
},
/**
* Re-compute the height of the carousel and its slides
*/
computeCarouselHeight() {
this.getCarouselHeight();
},
/**
* When the current page exceeds the carousel bounds, reset it to the maximum allowed
*/
Expand Down Expand Up @@ -735,6 +810,7 @@ export default {
this.attachMutationObserver();
this.computeCarouselWidth();
this.computeCarouselHeight();
this.transitionstart = getTransitionEnd();
this.$refs["VueCarousel-inner"].addEventListener(
Expand Down
17 changes: 16 additions & 1 deletion src/Slide.vue
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@
tabindex="-1"
:class="{
'VueCarousel-slide-active': isActive,
'VueCarousel-slide-center': isCenter
'VueCarousel-slide-center': isCenter,
'VueCarousel-slide-adjustableHeight': isAdjustableHeight
}"
>
<slot></slot>
Expand Down Expand Up @@ -66,6 +67,14 @@ export default {
const { perPage } = this.carousel;
if (perPage % 2 === 0 || !this.isActive) return false;
return this.activeSlides.indexOf(this._uid) === Math.floor(perPage / 2);
},
/**
* `isAdjustableHeight` describes if the carousel adjusts its height to the active slide(s)
* @return {Boolean}
*/
isAdjustableHeight() {
const { adjustableHeight } = this.carousel;
return adjustableHeight;
}
},
methods: {
Expand Down Expand Up @@ -97,4 +106,10 @@ export default {
-webkit-touch-callout: none;
outline: none;
}
.VueCarousel-slide-adjustableHeight {
display: table;
flex-basis: auto;
width: 100%;
}
</style>
55 changes: 37 additions & 18 deletions tests/client/components/__snapshots__/carousel.spec.js.snap
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

exports[`Carousel should apply custom slides per page when responsive param provided 1`] = `
".VueCarousel-wrapper
.VueCarousel-inner(role='listbox', style='transform: translate(0px, 0); visibility: hidden;')
.VueCarousel-inner(role='listbox', style='transform: translate(0px, 0); visibility: hidden; height: auto;')
|
.VueCarousel-pagination(style='display: none;')
ul.VueCarousel-dot-container(role='tablist')
Expand All @@ -12,7 +12,7 @@ exports[`Carousel should apply custom slides per page when responsive param prov

exports[`Carousel should apply default carousel width when element has 0 width 1`] = `
".VueCarousel-wrapper
.VueCarousel-inner(role='listbox', style='transform: translate(0px, 0); visibility: hidden;')
.VueCarousel-inner(role='listbox', style='transform: translate(0px, 0); visibility: hidden; height: auto;')
|
.VueCarousel-pagination(style='display: none;')
ul.VueCarousel-dot-container(role='tablist')
Expand All @@ -22,7 +22,7 @@ exports[`Carousel should apply default carousel width when element has 0 width 1

exports[`Carousel should be unable to advance backward by default 1`] = `
".VueCarousel-wrapper
.VueCarousel-inner(role='listbox', style='transform: translate(0px, 0); visibility: hidden;')
.VueCarousel-inner(role='listbox', style='transform: translate(0px, 0); visibility: hidden; height: auto;')
|
.VueCarousel-pagination(style='display: none;')
ul.VueCarousel-dot-container(role='tablist')
Expand All @@ -32,7 +32,7 @@ exports[`Carousel should be unable to advance backward by default 1`] = `

exports[`Carousel should be unable to advance forward by default (no slides added) 1`] = `
".VueCarousel-wrapper
.VueCarousel-inner(role='listbox', style='transform: translate(0px, 0); visibility: hidden;')
.VueCarousel-inner(role='listbox', style='transform: translate(0px, 0); visibility: hidden; height: auto;')
|
.VueCarousel-pagination(style='display: none;')
ul.VueCarousel-dot-container(role='tablist')
Expand All @@ -42,7 +42,7 @@ exports[`Carousel should be unable to advance forward by default (no slides adde

exports[`Carousel should begin autoplaying when option specified 1`] = `
".VueCarousel-wrapper
.VueCarousel-inner(role='listbox', style='transform: translate(0px, 0); visibility: hidden;')
.VueCarousel-inner(role='listbox', style='transform: translate(0px, 0); visibility: hidden; height: auto;')
.VueCarousel-slide.VueCarousel-slide-active.VueCarousel-slide-center(tabindex='-1')
.VueCarousel-slide(tabindex='-1')
|
Expand All @@ -58,7 +58,7 @@ exports[`Carousel should begin autoplaying when option specified 1`] = `

exports[`Carousel should decrease current page number by 1 when advance page backward is called 1`] = `
".VueCarousel-wrapper
.VueCarousel-inner(role='listbox', style='transform: translate(0px, 0); visibility: hidden;')
.VueCarousel-inner(role='listbox', style='transform: translate(0px, 0); visibility: hidden; height: auto;')
.VueCarousel-slide(tabindex='-1')
.VueCarousel-slide.VueCarousel-slide-active.VueCarousel-slide-center(tabindex='-1')
|
Expand All @@ -74,7 +74,7 @@ exports[`Carousel should decrease current page number by 1 when advance page bac

exports[`Carousel should decrease current slide number by 1 when advance slide backward is called 1`] = `
".VueCarousel-wrapper
.VueCarousel-inner(role='listbox', style='transform: translate(0px, 0); visibility: hidden;')
.VueCarousel-inner(role='listbox', style='transform: translate(0px, 0); visibility: hidden; height: auto;')
.VueCarousel-slide(tabindex='-1')
.VueCarousel-slide(tabindex='-1')
.VueCarousel-slide(tabindex='-1')
Expand All @@ -92,7 +92,7 @@ exports[`Carousel should decrease current slide number by 1 when advance slide b

exports[`Carousel should fall back to default slides per page when no responsive param provided 1`] = `
".VueCarousel-wrapper
.VueCarousel-inner(role='listbox', style='transform: translate(0px, 0); visibility: hidden;')
.VueCarousel-inner(role='listbox', style='transform: translate(0px, 0); visibility: hidden; height: auto;')
.VueCarousel-slide(tabindex='-1')
.VueCarousel-slide(tabindex='-1')
.VueCarousel-slide.VueCarousel-slide-active(tabindex='-1')
Expand All @@ -109,7 +109,7 @@ exports[`Carousel should fall back to default slides per page when no responsive

exports[`Carousel should increase current page number by 1 when advance page is called 1`] = `
".VueCarousel-wrapper
.VueCarousel-inner(role='listbox', style='transform: translate(0px, 0); visibility: hidden;')
.VueCarousel-inner(role='listbox', style='transform: translate(0px, 0); visibility: hidden; height: auto;')
.VueCarousel-slide(tabindex='-1')
.VueCarousel-slide.VueCarousel-slide-active.VueCarousel-slide-center(tabindex='-1')
.VueCarousel-slide(tabindex='-1')
Expand All @@ -131,7 +131,7 @@ exports[`Carousel should increase current page number by 1 when advance page is

exports[`Carousel should increase current page number by 1 when advance page is called with a non "backward" argument 1`] = `
".VueCarousel-wrapper
.VueCarousel-inner(role='listbox', style='transform: translate(0px, 0); visibility: hidden;')
.VueCarousel-inner(role='listbox', style='transform: translate(0px, 0); visibility: hidden; height: auto;')
.VueCarousel-slide(tabindex='-1')
.VueCarousel-slide.VueCarousel-slide-active.VueCarousel-slide-center(tabindex='-1')
.VueCarousel-slide(tabindex='-1')
Expand All @@ -153,7 +153,7 @@ exports[`Carousel should increase current page number by 1 when advance page is

exports[`Carousel should loop back to the start when loop is true and advance page non "backward" is called from the last page 1`] = `
".VueCarousel-wrapper
.VueCarousel-inner(role='listbox', style='transform: translate(0px, 0); visibility: hidden;')
.VueCarousel-inner(role='listbox', style='transform: translate(0px, 0); visibility: hidden; height: auto;')
.VueCarousel-slide.VueCarousel-slide-active.VueCarousel-slide-center(tabindex='-1')
.VueCarousel-slide(tabindex='-1')
|
Expand All @@ -169,7 +169,7 @@ exports[`Carousel should loop back to the start when loop is true and advance pa

exports[`Carousel should loop to the end when loop is true and advance page "backward" is called from the first page 1`] = `
".VueCarousel-wrapper
.VueCarousel-inner(role='listbox', style='transform: translate(0px, 0); visibility: hidden;')
.VueCarousel-inner(role='listbox', style='transform: translate(0px, 0); visibility: hidden; height: auto;')
.VueCarousel-slide(tabindex='-1')
.VueCarousel-slide.VueCarousel-slide-active.VueCarousel-slide-center(tabindex='-1')
|
Expand All @@ -185,7 +185,7 @@ exports[`Carousel should loop to the end when loop is true and advance page "bac

exports[`Carousel should mount successfully 1`] = `
".VueCarousel-wrapper
.VueCarousel-inner(role='listbox', style='transform: translate(0px, 0); visibility: hidden;')
.VueCarousel-inner(role='listbox', style='transform: translate(0px, 0); visibility: hidden; height: auto;')
|
.VueCarousel-pagination(style='display: none;')
ul.VueCarousel-dot-container(role='tablist')
Expand All @@ -195,7 +195,7 @@ exports[`Carousel should mount successfully 1`] = `

exports[`Carousel should not reset autoplay when switching slide with autoplayHoverPause 1`] = `
".VueCarousel-wrapper
.VueCarousel-inner(role='listbox', style='transform: translate(0px, 0); visibility: hidden;')
.VueCarousel-inner(role='listbox', style='transform: translate(0px, 0); visibility: hidden; height: auto;')
.VueCarousel-slide.VueCarousel-slide-active.VueCarousel-slide-center(tabindex='-1')
.VueCarousel-slide(tabindex='-1')
|
Expand All @@ -211,7 +211,7 @@ exports[`Carousel should not reset autoplay when switching slide with autoplayHo

exports[`Carousel should register 0 slides when 0 slides are added to the slots 1`] = `
".VueCarousel-wrapper
.VueCarousel-inner(role='listbox', style='transform: translate(0px, 0); visibility: hidden;')
.VueCarousel-inner(role='listbox', style='transform: translate(0px, 0); visibility: hidden; height: auto;')
|
.VueCarousel-pagination(style='display: none;')
ul.VueCarousel-dot-container(role='tablist')
Expand All @@ -221,7 +221,7 @@ exports[`Carousel should register 0 slides when 0 slides are added to the slots

exports[`Carousel should register 3 slides when 3 slides are added to the slots 1`] = `
".VueCarousel-wrapper
.VueCarousel-inner(role='listbox', style='transform: translate(0px, 0); visibility: hidden;')
.VueCarousel-inner(role='listbox', style='transform: translate(0px, 0); visibility: hidden; height: auto;')
.VueCarousel-slide.VueCarousel-slide-active(tabindex='-1')
.VueCarousel-slide.VueCarousel-slide-active(tabindex='-1')
.VueCarousel-slide(tabindex='-1')
Expand All @@ -238,7 +238,7 @@ exports[`Carousel should register 3 slides when 3 slides are added to the slots

exports[`Carousel should reset autoplay when switching slide without autoplayHoverPause 1`] = `
".VueCarousel-wrapper
.VueCarousel-inner(role='listbox', style='transform: translate(0px, 0); visibility: hidden;')
.VueCarousel-inner(role='listbox', style='transform: translate(0px, 0); visibility: hidden; height: auto;')
.VueCarousel-slide.VueCarousel-slide-active.VueCarousel-slide-center(tabindex='-1')
.VueCarousel-slide(tabindex='-1')
|
Expand All @@ -252,9 +252,28 @@ exports[`Carousel should reset autoplay when switching slide without autoplayHov
"
`;

exports[`Carousel should set carousel height to slide height 1`] = `
".VueCarousel-wrapper
.VueCarousel-inner(role='listbox', style='transform: translate(0px, 0); visibility: hidden; height: 200px;')
.VueCarousel-slide.VueCarousel-slide-adjustableHeight(tabindex='-1')
.VueCarousel-slide.VueCarousel-slide-adjustableHeight(tabindex='-1')
.VueCarousel-slide.VueCarousel-slide-active.VueCarousel-slide-center.VueCarousel-slide-adjustableHeight(tabindex='-1')
|
.VueCarousel-pagination(style='')
ul.VueCarousel-dot-container(role='tablist')
li.VueCarousel-dot(aria-hidden='false', role='presentation', aria-selected='false', style='margin-top: 20px; padding: 10px;')
button.VueCarousel-dot-button(type='button', role='button', aria-label='\`Item \${index}\`', title='Item 0', tabindex='0', style='width: 10px; height: 10px; background: rgb(239, 239, 239);')
li.VueCarousel-dot(aria-hidden='false', role='presentation', aria-selected='false', style='margin-top: 20px; padding: 10px;')
button.VueCarousel-dot-button(type='button', role='button', aria-label='\`Item \${index}\`', title='Item 1', tabindex='0', style='width: 10px; height: 10px; background: rgb(239, 239, 239);')
li.VueCarousel-dot.VueCarousel-dot--active(aria-hidden='false', role='presentation', aria-selected='true', style='margin-top: 20px; padding: 10px;')
button.VueCarousel-dot-button(type='button', role='button', aria-label='\`Item \${index}\`', title='Item 2', tabindex='0', style='width: 10px; height: 10px; background: rgb(0, 0, 0);')
//
"
`;

exports[`Carousel should unmount successfully 1`] = `
".VueCarousel-wrapper
.VueCarousel-inner(role='listbox', style='transform: translate(0px, 0); visibility: hidden;')
.VueCarousel-inner(role='listbox', style='transform: translate(0px, 0); visibility: hidden; height: auto;')
|
.VueCarousel-pagination(style='display: none;')
ul.VueCarousel-dot-container(role='tablist')
Expand Down
Loading

0 comments on commit f1e6314

Please sign in to comment.