Skip to content

Commit

Permalink
Merge pull request #453 from Tofandel/fix/ssr-issue
Browse files Browse the repository at this point in the history
fix: SSR hydration issue
  • Loading branch information
ismail9k authored Dec 11, 2024
2 parents db14166 + 5bd7d8f commit 31107b7
Show file tree
Hide file tree
Showing 4 changed files with 20 additions and 23 deletions.
17 changes: 6 additions & 11 deletions src/components/Carousel/Carousel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,6 @@ import {
ComponentInternalInstance,
watchEffect,
shallowReactive,
pushScopeId,
getCurrentInstance,
} from 'vue'

import { ARIA as ARIAComponent } from '@/components/ARIA'
Expand Down Expand Up @@ -122,6 +120,9 @@ export const Carousel = defineComponent({
const clonedSlidesCount = computed(() => Math.ceil(config.itemsToShow) + 1)

function updateBreakpointsConfig(): void {
if (!mounted.value) {
return
}
// Determine the width source based on the 'breakpointMode' config
const widthSource =
(fallbackConfig.value.breakpointMode === 'carousel'
Expand Down Expand Up @@ -268,12 +269,9 @@ export const Carousel = defineComponent({
})
}

updateBreakpointsConfig()
onMounted((): void => {
mounted.value = true
if (fallbackConfig.value.breakpointMode === 'carousel') {
updateBreakpointsConfig()
}
updateBreakpointsConfig()
initAutoplay()

if (root.value) {
Expand Down Expand Up @@ -568,6 +566,7 @@ export const Carousel = defineComponent({
slidesCount,
viewport,
slides,
clonedSlidesCount,
scrolledIndex,
currentSlide: currentSlideIndex,
maxSlide: maxSlideIndex,
Expand Down Expand Up @@ -666,7 +665,7 @@ export const Carousel = defineComponent({
*/
const trackTransform: ComputedRef<string> = computed(() => {
// Calculate the scrolled index with wrapping offset if applicable
const cloneOffset = config.wrapAround ? clonedSlidesCount.value : 0
const cloneOffset = config.wrapAround && mounted.value ? clonedSlidesCount.value : 0

// Determine direction multiplier for orientation
const directionMultiplier = isReversed.value ? -1 : 1
Expand Down Expand Up @@ -705,13 +704,9 @@ export const Carousel = defineComponent({
const addonsElements = slotAddons?.(data) || []

if (config.wrapAround) {
// Ensure scoped CSS tracks properly
const scopeId = output.length > 0 ? output[0].scopeId : null
pushScopeId(scopeId)
const toShow = clonedSlidesCount.value
const slidesBefore = createCloneSlides({ slides, position: 'before', toShow })
const slidesAfter = createCloneSlides({ slides, position: 'after', toShow })
pushScopeId(getCurrentInstance()!.vnode.scopeId)
output = [...slidesBefore, ...output, ...slidesAfter]
}

Expand Down
1 change: 1 addition & 0 deletions src/components/Carousel/Carousel.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ export type InjectedCarousel = Reactive<{
viewport: Ref<Element | null>
slides: ShallowReactive<Array<ComponentInternalInstance>>
slidesCount: ComputedRef<number>
clonedSlidesCount: ComputedRef<number>
currentSlide: Ref<number>
scrolledIndex: Ref<number>
maxSlide: ComputedRef<number>
Expand Down
21 changes: 11 additions & 10 deletions src/utils/createCloneSlides.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
import { cloneVNode, ComponentInternalInstance, h } from 'vue'

import { Slide } from '@/components/Slide'
import { cloneVNode, ComponentInternalInstance, VNode } from 'vue'

type CreateCloneSlidesArgs = {
slides: Array<ComponentInternalInstance>
Expand All @@ -9,24 +7,27 @@ type CreateCloneSlidesArgs = {
}

export function createCloneSlides({ slides, position, toShow }: CreateCloneSlidesArgs) {
const clones = []
const clones: VNode[] = []
const isBefore = position === 'before'
const start = isBefore ? -toShow : 0
const end = isBefore ? 0 : toShow

if (slides.length <= 0) {
return clones
}

for (let i = start; i < end; i++) {
const index = isBefore ? i : slides.length > 0 ? i + slides.length : i + 99999
const index = isBefore ? i : i + slides.length
const props = {
index,
isClone: true,
id: undefined, // Make sure we don't duplicate the id which would be invalid html
key: `clone-${position}-${i}`,
}
clones.push(
slides.length > 0
? cloneVNode(slides[(i % slides.length + slides.length) % slides.length].vnode, props)
: h(Slide, props)
)
const vnode = slides[((i % slides.length) + slides.length) % slides.length].vnode
const clone = cloneVNode(vnode, props)
clone.el = null
clones.push(clone)
}

return clones
Expand Down
4 changes: 2 additions & 2 deletions tests/integration/__snapshots__/carousel.spec.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ exports[`SSR Carousel > renders server side properly 1`] = `
</div>"
`;
exports[`SSR Carousel > renders server side properly 2`] = `"<div id="app"><section class="carousel is-ltr" style="" dir="ltr" aria-label="Gallery" tabindex="0"><div class="carousel__viewport"><ol class="carousel__track" style="transform:translateX(0px);"><li style="width:50%;" class="carousel__slide carousel__slide--clone" aria-hidden="true"></li><li style="width:50%;" class="carousel__slide carousel__slide--clone" aria-hidden="true"></li><li style="width:50%;" class="carousel__slide carousel__slide--clone" aria-hidden="true"></li><!--[--><li style="width:50%;" class="carousel__slide carousel__slide--visible carousel__slide--prev" id="v-0">1 <input type="text"></li><li style="width:50%;" class="carousel__slide carousel__slide--visible carousel__slide--active" id="v-1">2 <input type="text"></li><li style="width:50%;" class="carousel__slide carousel__slide--visible carousel__slide--next" id="v-2">3 <input type="text"></li><li style="width:50%;" class="carousel__slide" id="v-3">4 <input type="text"></li><li style="width:50%;" class="carousel__slide" id="v-4">5 <input type="text"></li><!--]--><li style="width:50%;" class="carousel__slide carousel__slide--clone" aria-hidden="true"></li><li style="width:50%;" class="carousel__slide carousel__slide--clone" aria-hidden="true"></li><li style="width:50%;" class="carousel__slide carousel__slide--clone" aria-hidden="true"></li></ol></div><!--[--><!--[--><button type="button" aria-label="Navigate to previous slide" title="Navigate to previous slide" class="carousel__prev"><svg class="carousel__icon" viewBox="0 0 24 24" role="img" aria-label="Arrow pointing to the left"><title>Arrow pointing to the left</title><path d="M15.41 16.59L10.83 12l4.58-4.59L14 6l-6 6 6 6 1.41-1.41z"></path></svg></button><button type="button" aria-label="Navigate to next slide" title="Navigate to next slide" class="carousel__next"><svg class="carousel__icon" viewBox="0 0 24 24" role="img" aria-label="Arrow pointing to the right"><title>Arrow pointing to the right</title><path d="M8.59 16.59L13.17 12 8.59 7.41 10 6l6 6-6 6-1.41-1.41z"></path></svg></button><!--]--><ol class="carousel__pagination"><li class="carousel__pagination-item"><button type="button" class="carousel__pagination-button" aria-label="Navigate to slide 1" aria-pressed="false" aria-controls="v-0" title="Navigate to slide 1"></button></li><li class="carousel__pagination-item"><button type="button" class="carousel__pagination-button carousel__pagination-button--active" aria-label="Navigate to slide 2" aria-pressed="true" aria-controls="v-1" title="Navigate to slide 2"></button></li><li class="carousel__pagination-item"><button type="button" class="carousel__pagination-button" aria-label="Navigate to slide 3" aria-pressed="false" aria-controls="v-2" title="Navigate to slide 3"></button></li><li class="carousel__pagination-item"><button type="button" class="carousel__pagination-button" aria-label="Navigate to slide 4" aria-pressed="false" aria-controls="v-3" title="Navigate to slide 4"></button></li><li class="carousel__pagination-item"><button type="button" class="carousel__pagination-button" aria-label="Navigate to slide 5" aria-pressed="false" aria-controls="v-4" title="Navigate to slide 5"></button></li></ol><!--]--><div class="carousel__liveregion carousel__sr-only" aria-live="polite" aria-atomic="true">Item 2 of 5</div></section></div>"`;
exports[`SSR Carousel > renders server side properly 2`] = `"<div id="app"><section class="carousel is-ltr" style="" dir="ltr" aria-label="Gallery" tabindex="0"><div class="carousel__viewport"><ol class="carousel__track" style="transform:translateX(0px);"><!--[--><li style="width:50%;" class="carousel__slide carousel__slide--visible carousel__slide--prev" id="v-0">1 <input type="text"></li><li style="width:50%;" class="carousel__slide carousel__slide--visible carousel__slide--active" id="v-1">2 <input type="text"></li><li style="width:50%;" class="carousel__slide carousel__slide--visible carousel__slide--next" id="v-2">3 <input type="text"></li><li style="width:50%;" class="carousel__slide" id="v-3">4 <input type="text"></li><li style="width:50%;" class="carousel__slide" id="v-4">5 <input type="text"></li><!--]--></ol></div><!--[--><!--[--><button type="button" aria-label="Navigate to previous slide" title="Navigate to previous slide" class="carousel__prev"><svg class="carousel__icon" viewBox="0 0 24 24" role="img" aria-label="Arrow pointing to the left"><title>Arrow pointing to the left</title><path d="M15.41 16.59L10.83 12l4.58-4.59L14 6l-6 6 6 6 1.41-1.41z"></path></svg></button><button type="button" aria-label="Navigate to next slide" title="Navigate to next slide" class="carousel__next"><svg class="carousel__icon" viewBox="0 0 24 24" role="img" aria-label="Arrow pointing to the right"><title>Arrow pointing to the right</title><path d="M8.59 16.59L13.17 12 8.59 7.41 10 6l6 6-6 6-1.41-1.41z"></path></svg></button><!--]--><ol class="carousel__pagination"><li class="carousel__pagination-item"><button type="button" class="carousel__pagination-button" aria-label="Navigate to slide 1" aria-pressed="false" aria-controls="v-0" title="Navigate to slide 1"></button></li><li class="carousel__pagination-item"><button type="button" class="carousel__pagination-button carousel__pagination-button--active" aria-label="Navigate to slide 2" aria-pressed="true" aria-controls="v-1" title="Navigate to slide 2"></button></li><li class="carousel__pagination-item"><button type="button" class="carousel__pagination-button" aria-label="Navigate to slide 3" aria-pressed="false" aria-controls="v-2" title="Navigate to slide 3"></button></li><li class="carousel__pagination-item"><button type="button" class="carousel__pagination-button" aria-label="Navigate to slide 4" aria-pressed="false" aria-controls="v-3" title="Navigate to slide 4"></button></li><li class="carousel__pagination-item"><button type="button" class="carousel__pagination-button" aria-label="Navigate to slide 5" aria-pressed="false" aria-controls="v-4" title="Navigate to slide 5"></button></li></ol><!--]--><div class="carousel__liveregion carousel__sr-only" aria-live="polite" aria-atomic="true">Item 2 of 5</div></section></div>"`;
exports[`SSR Carousel > renders slotted server side properly 1`] = `
"<div id="app">
Expand All @@ -59,7 +59,7 @@ exports[`SSR Carousel > renders slotted server side properly 1`] = `
</div>"
`;
exports[`SSR Carousel > renders slotted server side properly 2`] = `"<div id="app"><section class="carousel is-ltr" style="" dir="ltr" aria-label="Gallery" tabindex="0"><div class="carousel__viewport"><ol class="carousel__track" style="transform:translateX(0px);"><li style="width:100%;" class="carousel__slide carousel__slide--clone" aria-hidden="true"></li><li style="width:100%;" class="carousel__slide carousel__slide--clone carousel__slide--prev" aria-hidden="true"></li><!--[--><!--[--><li style="width:100%;" class="carousel__slide carousel__slide--visible carousel__slide--active" id="v-0">1</li><li style="width:100%;" class="carousel__slide carousel__slide--next" id="v-1">2</li><li style="width:100%;" class="carousel__slide" id="v-2">3</li><li style="width:100%;" class="carousel__slide" id="v-3">4</li><li style="width:100%;" class="carousel__slide" id="v-4">5</li><!--]--><!--]--><li style="width:100%;" class="carousel__slide carousel__slide--clone" aria-hidden="true"></li><li style="width:100%;" class="carousel__slide carousel__slide--clone" aria-hidden="true"></li></ol></div><!--[--><!--]--><div class="carousel__liveregion carousel__sr-only" aria-live="polite" aria-atomic="true">Item 1 of 5</div></section></div>"`;
exports[`SSR Carousel > renders slotted server side properly 2`] = `"<div id="app"><section class="carousel is-ltr" style="" dir="ltr" aria-label="Gallery" tabindex="0"><div class="carousel__viewport"><ol class="carousel__track" style="transform:translateX(0px);"><!--[--><!--[--><li style="width:100%;" class="carousel__slide carousel__slide--visible carousel__slide--active" id="v-0">1</li><li style="width:100%;" class="carousel__slide carousel__slide--next" id="v-1">2</li><li style="width:100%;" class="carousel__slide" id="v-2">3</li><li style="width:100%;" class="carousel__slide" id="v-3">4</li><li style="width:100%;" class="carousel__slide" id="v-4">5</li><!--]--><!--]--></ol></div><!--[--><!--]--><div class="carousel__liveregion carousel__sr-only" aria-live="polite" aria-atomic="true">Item 1 of 5</div></section></div>"`;
exports[`Wrap around Carousel.ts > renders wrapAround correctly 1`] = `
"<section class="carousel is-ltr" dir="ltr" aria-label="Gallery" tabindex="0">
Expand Down

0 comments on commit 31107b7

Please sign in to comment.