diff --git a/README.md b/README.md
index b6b4903..de7ac88 100644
--- a/README.md
+++ b/README.md
@@ -35,6 +35,7 @@ For more examples, see the demo: https://vue-ssr-carousel.netlify.app.
- `slides-per-page` (`1`) - How many slides are shown per page.
- `gutter` (`20`) - The size of the space between slides. This can a number or any CSS resolvable string. See https://vue-ssr-carousel.netlify.app/gutters.
- `responsive` (`[]`) - Adjust settings at breakpoints. See https://vue-ssr-carousel.netlify.app/responsive.
+- `loop` (`false`) - Boolean to enable looping / infinite scroll.
- `paginate-by-slide` (`false`) - When `false`, dragging the carousel or interacting with the arrows will advance a full page of slides at a time. When `true`, the carousel will come to a rest at each slide.
- `show-arrows` (`false`) - Whether to show back/forward arrows. See https://vue-ssr-carousel.netlify.app/ui.
- `show-dots` (`false`) - Whether to show dot style pagination dots. See https://vue-ssr-carousel.netlify.app/ui.
diff --git a/demo/components/layout/nav.vue b/demo/components/layout/nav.vue
index 2815e56..a769086 100644
--- a/demo/components/layout/nav.vue
+++ b/demo/components/layout/nav.vue
@@ -19,6 +19,7 @@ export default
{ title: 'Responsive', path: '/responsive' }
{ title: 'Gutters', path: '/gutters' }
{ title: 'UI', path: '/ui' }
+ { title: 'Looping', path: '/looping' }
{ title: 'Miscellaneous', path: '/misc' }
]
diff --git a/demo/components/slide.vue b/demo/components/slide.vue
index 20bc501..eda81b8 100644
--- a/demo/components/slide.vue
+++ b/demo/components/slide.vue
@@ -3,7 +3,7 @@
.slide: div
- .title Slide {{ index }}
+ .title(v-if='index') Slide {{ index }}
slot
@@ -36,6 +36,8 @@ export default
flex-center()
text-align center
fluid-space padding 's'
+ > div
+ width 100%
// Increase slide text size
.title
diff --git a/demo/content/looping.md b/demo/content/looping.md
new file mode 100644
index 0000000..dda2a16
--- /dev/null
+++ b/demo/content/looping.md
@@ -0,0 +1,79 @@
+---
+title: 'Looping'
+---
+
+## Basic looping
+
+Looping is also known as `wrapAround` or `infinite` in other carousels.
+
+
+
+
+
+
+
+```vue
+
+
+
+
+
+```
+
+## Looping with multiple slides per page
+
+Note how the incomplete 2nd page is handled. The 3rd and 1st slide are shown simulataneously. On the next advance forward, the track advances a half width so that the *new* first page contains the 1st and 2nd slide.
+
+
+
+
+
+
+
+```vue
+
+
+
+
+
+```
+
+## Cloned slides can contain components
+
+In this case, we're using [vue-visual](https://github.com/BKWLD/vue-visual) components to render image assets. Note how lazy loading prevents the loading of the second image until you advance forward.
+
+
+
+
+
+
+
+
+
+
+
+
+```vue
+
+
+
+
+
+
+
+
+
+
+```
diff --git a/demo/package.json b/demo/package.json
index 34ab1bf..6202400 100644
--- a/demo/package.json
+++ b/demo/package.json
@@ -21,6 +21,7 @@
"prism-themes": "^1.9.0",
"vue-balance-text": "^1.2.3",
"vue-detachable-header": "^0.2.0",
- "vue-unorphan": "^1.2.3"
+ "vue-unorphan": "^1.2.3",
+ "vue-visual": "^2.6.0"
}
}
diff --git a/demo/pages/_page.vue b/demo/pages/_page.vue
index 95f1a78..2c3a3bd 100644
--- a/demo/pages/_page.vue
+++ b/demo/pages/_page.vue
@@ -21,12 +21,14 @@ article
diff --git a/src/ssr-carousel-dots.vue b/src/ssr-carousel-dots.vue
index 041af16..3c54127 100644
--- a/src/ssr-carousel-dots.vue
+++ b/src/ssr-carousel-dots.vue
@@ -6,7 +6,7 @@
button.ssr-carousel-dot-button(
v-for='i in pages' :key='i'
:aria-label='`Page ${i}`'
- :disabled='index == i - 1'
+ :disabled='boundedIndex == i - 1'
@click='$emit("goto", i - 1)')
slot(v-if='$slots.dot' name='dot')
span.ssr-carousel-dot-icon(v-else)
@@ -19,7 +19,7 @@
export default
props:
- index: Number
+ boundedIndex: Number
pages: Number
diff --git a/src/ssr-carousel-track.vue b/src/ssr-carousel-track.vue
index 6370c08..0b35b0f 100644
--- a/src/ssr-carousel-track.vue
+++ b/src/ssr-carousel-track.vue
@@ -9,19 +9,20 @@ export default
props:
dragging: Boolean
currentX: Number
+ trackOffset: Number
+ slides: Array
computed:
# Styles that are used to position the track
- styles: -> transform: "translateX(#{@currentX}px)"
+ styles: -> transform: "translateX(#{@currentX + @trackOffset}px)"
# Render the track and slotted slides
render: (create) ->
# Wrap the slides in ssr-carousel-slide functional components
- children = @$slots.default.map (child) ->
- if child.text then child # Text nodes like newlines
- else create SsrCarouselSlide, { parent: this }, [ child ]
+ children = @slides.map (child) ->
+ create SsrCarouselSlide, { parent: this }, [ child ]
# Create the track div
create 'div',
diff --git a/src/ssr-carousel.vue b/src/ssr-carousel.vue
index 8ccddd5..c22e54c 100644
--- a/src/ssr-carousel.vue
+++ b/src/ssr-carousel.vue
@@ -7,23 +7,27 @@
//- Render generated styles
component(is='style' v-html='instanceStyles')
- //- The overflow mask and drag target
- .ssr-carousel-mask(
- :class='{ pressing, disabled }'
- v-on='maskListeners')
-
- //- The container of the slides that animates
- ssr-carousel-track(
- ref='track'
- v-bind='{ dragging, currentX }')
-
- //- Slides are injected here
- slot
+ //- Container so that arrows can be centered relative to slides but not be
+ //- within the mask's overflow:hidden (which prevents overriding styles from
+ //- positioning outside of carousel).
+ .ssr-carousel-slides
+
+ //- The overflow mask and drag target
+ .ssr-carousel-mask(
+ :class='{ pressing, disabled }'
+ v-on='maskListeners')
+
+ //- The container of the slides that animates
+ ssr-carousel-track(
+ ref='track'
+ :slides='slides'
+ v-bind='{ dragging, currentX, trackOffset }')
//- Back / Next navigation
ssr-carousel-arrows(
v-if='showArrows'
v-bind='{ index, pages }'
+ :loop='loop'
@back='back'
@next='next')
template(#back): slot(name='back-arrow')
@@ -32,8 +36,8 @@
//- Dots navigation
ssr-carousel-dots(
v-if='showDots'
- v-bind='{ index, pages }'
- @goto='goto')
+ v-bind='{ boundedIndex, pages }'
+ @goto='gotoDot')
template(#dot): slot(name='dot')
@@ -51,6 +55,7 @@ import SsrCarouselTrack from './ssr-carousel-track'
import autoplay from './concerns/autoplay'
import dragging from './concerns/dragging'
import focus from './concerns/focus'
+import looping from './concerns/looping'
import pagination from './concerns/pagination'
import responsive from './concerns/responsive'
import tweening from './concerns/tweening'
@@ -64,6 +69,7 @@ export default
autoplay
dragging
focus
+ looping
pagination
responsive
tweening
@@ -82,8 +88,11 @@ export default
showDots: Boolean
computed:
+
+ # Determine whether to create hover event bindings
watchesHover: -> @autoplayDelay > 0
+ # Create event bindings
maskListeners: ->
return {} if @disabled
{
@@ -105,6 +114,10 @@ export default
.ssr-carousel
touch-action pan-y
+// Posiition arrows relative to this
+.ssr-carousel-slides
+ position relative
+
// Mask around slides
.ssr-carousel-mask
overflow hidden