Skip to content

Commit c648dd6

Browse files
committed
Add SSR support for all library wrappers.
1 parent 3672368 commit c648dd6

File tree

23 files changed

+255
-191
lines changed

23 files changed

+255
-191
lines changed

packages/embla-carousel-auto-height/src/components/AutoHeight.ts

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@ import { OptionsType } from './Options'
22
import {
33
EmblaEventType,
44
CreatePluginType,
5-
EmblaCarouselType
5+
EmblaCarouselType,
6+
OptionsHandlerType
67
} from 'embla-carousel'
78

89
declare module 'embla-carousel' {
@@ -20,15 +21,19 @@ function AutoHeight(userOptions: AutoHeightOptionsType = {}): AutoHeightType {
2021
let slideHeights: number[] = []
2122
const heightEvents: EmblaEventType[] = ['select', 'slidefocus']
2223

23-
function init(emblaApiInstance: EmblaCarouselType): void {
24+
function init(
25+
emblaApiInstance: EmblaCarouselType,
26+
_: OptionsHandlerType,
27+
isSsr: boolean
28+
): void {
2429
emblaApi = emblaApiInstance
2530

2631
const {
2732
options: { axis },
2833
slideRects
2934
} = emblaApi.internalEngine()
3035

31-
if (axis === 'y') return
36+
if (isSsr || axis === 'y') return
3237

3338
slideHeights = slideRects.map((slideRect) => slideRect.height)
3439

packages/embla-carousel-auto-scroll/src/components/AutoScroll.ts

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,8 @@ function AutoScroll(userOptions: AutoScrollOptionsType = {}): AutoScrollType {
4848

4949
function init(
5050
emblaApiInstance: EmblaCarouselType,
51-
optionsHandler: OptionsHandlerType
51+
optionsHandler: OptionsHandlerType,
52+
isSsr: boolean
5253
): void {
5354
emblaApi = emblaApiInstance
5455

@@ -57,7 +58,7 @@ function AutoScroll(userOptions: AutoScrollOptionsType = {}): AutoScrollType {
5758
const allOptions = mergeOptions(optionsBase, userOptions)
5859
options = optionsAtMedia(allOptions)
5960

60-
if (emblaApi.snapList().length <= 1) return
61+
if (isSsr || emblaApi.snapList().length <= 1) return
6162

6263
startDelay = options.startDelay
6364
destroyed = false
@@ -107,13 +108,14 @@ function AutoScroll(userOptions: AutoScrollOptionsType = {}): AutoScrollType {
107108
}
108109

109110
function startAutoScroll(): void {
111+
const engine = emblaApi.internalEngine()
112+
const { ownerWindow } = engine.nodeHandler
113+
114+
if (!ownerWindow) return
110115
if (destroyed) return
111116
if (autoScrollActive) return
112117
emblaApi.emit('autoscroll:play', null)
113118

114-
const engine = emblaApi.internalEngine()
115-
const { ownerWindow } = engine
116-
117119
timerId = ownerWindow.setTimeout(() => {
118120
engine.scrollBody = createAutoScrollBehaviour(engine)
119121
engine.animation.start()
@@ -123,13 +125,14 @@ function AutoScroll(userOptions: AutoScrollOptionsType = {}): AutoScrollType {
123125
}
124126

125127
function stopAutoScroll(): void {
128+
const engine = emblaApi.internalEngine()
129+
const { ownerWindow } = engine.nodeHandler
130+
131+
if (!ownerWindow) return
126132
if (destroyed) return
127133
if (!autoScrollActive) return
128134
emblaApi.emit('autoscroll:stop', null)
129135

130-
const engine = emblaApi.internalEngine()
131-
const { ownerWindow } = engine
132-
133136
engine.scrollBody = defaultScrollBehaviour
134137
ownerWindow.clearTimeout(timerId)
135138
timerId = 0

packages/embla-carousel-autoplay/src/components/Autoplay.ts

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,8 @@ function Autoplay(userOptions: AutoplayOptionsType = {}): AutoplayType {
5555

5656
function init(
5757
emblaApiInstance: EmblaCarouselType,
58-
optionsHandler: OptionsHandlerType
58+
optionsHandler: OptionsHandlerType,
59+
isSsr: boolean
5960
): void {
6061
emblaApi = emblaApiInstance
6162

@@ -64,17 +65,20 @@ function Autoplay(userOptions: AutoplayOptionsType = {}): AutoplayType {
6465
const allOptions = mergeOptions(optionsBase, userOptions)
6566
options = optionsAtMedia(allOptions)
6667

67-
if (emblaApi.snapList().length <= 1) return
68+
if (isSsr || emblaApi.snapList().length <= 1) return
6869

6970
jump = options.jump
7071
destroyed = false
7172
delay = normalizeDelay(emblaApi, options.delay)
7273

73-
const { eventStore, ownerDocument } = emblaApi.internalEngine()
74+
const { eventStore, nodeHandler } = emblaApi.internalEngine()
75+
const { ownerDocument } = nodeHandler
7476
const isDraggable = emblaApi.internalEngine().options.draggable
7577
const root = getAutoplayRootNode(emblaApi, options.rootNode)
7678

77-
eventStore.add(ownerDocument, 'visibilitychange', onVisibilityChange)
79+
if (ownerDocument) {
80+
eventStore.add(ownerDocument, 'visibilitychange', onVisibilityChange)
81+
}
7882

7983
if (isDraggable) {
8084
emblaApi.on('pointerdown', onPointerDown)
@@ -115,15 +119,19 @@ function Autoplay(userOptions: AutoplayOptionsType = {}): AutoplayType {
115119
}
116120

117121
function setTimer(): void {
118-
const { ownerWindow } = emblaApi.internalEngine()
122+
const { ownerWindow } = emblaApi.internalEngine().nodeHandler
123+
if (!ownerWindow) return
124+
119125
ownerWindow.clearTimeout(timerId)
120126
timerId = ownerWindow.setTimeout(next, delay[emblaApi.selectedSnap()])
121127
timerStartTime = new Date().getTime()
122128
emblaApi.emit('autoplay:timerset', null)
123129
}
124130

125131
function clearTimer(): void {
126-
const { ownerWindow } = emblaApi.internalEngine()
132+
const { ownerWindow } = emblaApi.internalEngine().nodeHandler
133+
if (!ownerWindow) return
134+
127135
ownerWindow.clearTimeout(timerId)
128136
timerId = 0
129137
timerStartTime = null
@@ -160,7 +168,8 @@ function Autoplay(userOptions: AutoplayOptionsType = {}): AutoplayType {
160168
}
161169

162170
function documentIsHidden(): boolean {
163-
const { ownerDocument } = emblaApi.internalEngine()
171+
const { ownerDocument } = emblaApi.internalEngine().nodeHandler
172+
if (!ownerDocument) return false
164173
return ownerDocument.visibilityState === 'hidden'
165174
}
166175

packages/embla-carousel-class-names/src/components/ClassNames.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,8 @@ function ClassNames(userOptions: ClassNamesOptionsType = {}): ClassNamesType {
3838

3939
function init(
4040
emblaApiInstance: EmblaCarouselType,
41-
optionsHandler: OptionsHandlerType
41+
optionsHandler: OptionsHandlerType,
42+
isSsr: boolean
4243
): void {
4344
emblaApi = emblaApiInstance
4445

@@ -47,6 +48,8 @@ function ClassNames(userOptions: ClassNamesOptionsType = {}): ClassNamesType {
4748
const allOptions = mergeOptions(optionsBase, userOptions)
4849
options = optionsAtMedia(allOptions)
4950

51+
if (isSsr) return
52+
5053
root = emblaApi.rootNode()
5154
slides = emblaApi.slideNodes()
5255

packages/embla-carousel-fade/src/components/Fade.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { isNumber, clampNumber } from './utils'
33
import {
44
CreatePluginType,
55
EmblaCarouselType,
6+
OptionsHandlerType,
67
ScrollBodyType
78
} from 'embla-carousel'
89

@@ -32,9 +33,15 @@ function Fade(userOptions: FadeOptionsType = {}): FadeType {
3233
let defaultSettledBehaviour: ScrollBodyType['settled']
3334
let defaultProgressBehaviour: EmblaCarouselType['scrollProgress']
3435

35-
function init(emblaApiInstance: EmblaCarouselType): void {
36+
function init(
37+
emblaApiInstance: EmblaCarouselType,
38+
_: OptionsHandlerType,
39+
isSsr: boolean
40+
): void {
3641
emblaApi = emblaApiInstance
3742

43+
if (isSsr) return
44+
3845
const selectedSnap = emblaApi.selectedSnap()
3946
const { scrollBody, containerRect, axis } = emblaApi.internalEngine()
4047
const containerSize = axis.measureSize(containerRect)

packages/embla-carousel-react/src/components/useEmblaCarousel.ts

Lines changed: 18 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,5 @@
11
import { useRef, useEffect, useState, useCallback } from 'react'
2-
import {
3-
areOptionsEqual,
4-
arePluginsEqual,
5-
canUseDOM
6-
} from 'embla-carousel-reactive-utils'
2+
import { areOptionsEqual, arePluginsEqual } from 'embla-carousel-reactive-utils'
73
import EmblaCarousel, {
84
EmblaCarouselType,
95
EmblaOptionsType,
@@ -16,21 +12,27 @@ export type EmblaViewportRefType = <ViewportElement extends HTMLElement>(
1612

1713
export type UseEmblaCarouselType = [
1814
EmblaViewportRefType,
19-
EmblaCarouselType | undefined
15+
EmblaCarouselType | undefined,
16+
EmblaCarouselType
2017
]
2118

2219
function useEmblaCarousel(
2320
options: EmblaOptionsType = {},
2421
plugins: EmblaPluginType[] = []
2522
): UseEmblaCarouselType {
23+
EmblaCarousel.globalOptions = useEmblaCarousel.globalOptions
24+
2625
const storedOptions = useRef(options)
2726
const storedPlugins = useRef(plugins)
28-
const [emblaApi, setEmblaApi] = useState<EmblaCarouselType>()
27+
28+
const serverApi = useRef(EmblaCarousel(null, options, plugins))
29+
const [clientApi, setClientApi] = useState<EmblaCarouselType>()
2930
const [viewport, setViewport] = useState<HTMLElement>()
3031

3132
const reInit = useCallback(() => {
32-
if (emblaApi) emblaApi.reInit(storedOptions.current, storedPlugins.current)
33-
}, [emblaApi])
33+
if (!clientApi) return
34+
clientApi.reInit(storedOptions.current, storedPlugins.current)
35+
}, [clientApi])
3436

3537
useEffect(() => {
3638
if (areOptionsEqual(storedOptions.current, options)) return
@@ -45,21 +47,21 @@ function useEmblaCarousel(
4547
}, [plugins, reInit])
4648

4749
useEffect(() => {
48-
if (canUseDOM() && viewport) {
50+
if (viewport) {
4951
EmblaCarousel.globalOptions = useEmblaCarousel.globalOptions
50-
const newEmblaApi = EmblaCarousel(
52+
const newClientApi = EmblaCarousel(
5153
viewport,
5254
storedOptions.current,
5355
storedPlugins.current
5456
)
55-
setEmblaApi(newEmblaApi)
56-
return () => newEmblaApi.destroy()
57+
setClientApi(newClientApi)
58+
return () => newClientApi.destroy()
5759
} else {
58-
setEmblaApi(undefined)
60+
setClientApi(undefined)
5961
}
60-
}, [viewport, setEmblaApi])
62+
}, [viewport])
6163

62-
return [<EmblaViewportRefType>setViewport, emblaApi]
64+
return [<EmblaViewportRefType>setViewport, clientApi, serverApi.current]
6365
}
6466

6567
declare namespace useEmblaCarousel {

packages/embla-carousel-reactive-utils/src/components/utils.ts

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,6 @@ export function isRecord(
1010
return isObject(subject) || Array.isArray(subject)
1111
}
1212

13-
export function canUseDOM(): boolean {
14-
return !!(
15-
typeof window !== 'undefined' &&
16-
window.document &&
17-
window.document.createElement
18-
)
19-
}
20-
2113
export function areOptionsEqual(
2214
optionsA: Record<string, unknown>,
2315
optionsB: Record<string, unknown>

packages/embla-carousel-reactive-utils/src/index.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
export {
2-
canUseDOM,
32
areOptionsEqual,
43
sortAndMapPluginToOptions,
54
arePluginsEqual

packages/embla-carousel-solid/src/components/createEmblaCarousel.ts renamed to packages/embla-carousel-solid/src/components/useEmblaCarousel.ts

Lines changed: 20 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,7 @@ import EmblaCarousel, {
33
type EmblaOptionsType,
44
type EmblaPluginType
55
} from 'embla-carousel'
6-
import {
7-
areOptionsEqual,
8-
arePluginsEqual,
9-
canUseDOM
10-
} from 'embla-carousel-reactive-utils'
6+
import { areOptionsEqual, arePluginsEqual } from 'embla-carousel-reactive-utils'
117
import {
128
Accessor,
139
Setter,
@@ -17,65 +13,68 @@ import {
1713
onCleanup
1814
} from 'solid-js'
1915

20-
export type CreateEmblaCarouselType = [
16+
export type UseEmblaCarouselType = [
2117
Setter<HTMLElement | undefined>,
22-
Accessor<EmblaCarouselType | undefined>
18+
Accessor<EmblaCarouselType | undefined>,
19+
EmblaCarouselType
2320
]
2421

25-
function createEmblaCarousel(
22+
function useEmblaCarousel(
2623
options?: Accessor<EmblaOptionsType>,
2724
plugins?: Accessor<EmblaPluginType[]>
28-
): CreateEmblaCarouselType {
25+
): UseEmblaCarouselType {
26+
EmblaCarousel.globalOptions = useEmblaCarousel.globalOptions
27+
2928
let storedOptions = options ? options() : {}
3029
let storedPlugins = plugins ? plugins() : []
31-
const [emblaApi, setEmblaApi] = createSignal<EmblaCarouselType>()
30+
31+
const serverApi = EmblaCarousel(null, storedOptions, storedPlugins)
32+
const [clientApi, setClientApi] = createSignal<EmblaCarouselType>()
3233
const [viewport, setViewport] = createSignal<HTMLElement>()
3334

3435
function reInit(): void {
35-
const api = emblaApi()
36+
const api = clientApi()
3637
if (api) api.reInit(storedOptions, storedPlugins)
3738
}
3839

3940
createEffect(
4041
on(viewport, (viewport) => {
41-
if (canUseDOM() && viewport) {
42-
EmblaCarousel.globalOptions = createEmblaCarousel.globalOptions
42+
if (viewport) {
43+
EmblaCarousel.globalOptions = useEmblaCarousel.globalOptions
4344
const newEmblaApi = EmblaCarousel(
4445
viewport,
4546
storedOptions,
4647
storedPlugins
4748
)
48-
setEmblaApi(newEmblaApi)
49+
setClientApi(newEmblaApi)
4950
onCleanup(() => newEmblaApi.destroy())
5051
} else {
51-
setEmblaApi(undefined)
52+
setClientApi(undefined)
5253
}
5354
})
5455
)
5556

5657
createEffect(() => {
57-
if (!canUseDOM()) return
5858
const newOptions = options ? options() : {}
5959
if (areOptionsEqual(storedOptions, newOptions)) return
6060
storedOptions = newOptions
6161
reInit()
6262
})
6363

6464
createEffect(() => {
65-
if (!canUseDOM()) return
6665
const newPlugins = plugins ? plugins() : []
6766
if (arePluginsEqual(storedPlugins, newPlugins)) return
6867
storedPlugins = newPlugins
6968
reInit()
7069
})
7170

72-
return [setViewport, emblaApi]
71+
return [setViewport, clientApi, serverApi]
7372
}
7473

75-
declare namespace createEmblaCarousel {
74+
declare namespace useEmblaCarousel {
7675
let globalOptions: EmblaOptionsType | undefined
7776
}
7877

79-
createEmblaCarousel.globalOptions = undefined
78+
useEmblaCarousel.globalOptions = undefined
8079

81-
export default createEmblaCarousel
80+
export default useEmblaCarousel
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
1-
export { CreateEmblaCarouselType } from './components/createEmblaCarousel'
2-
export { default } from './components/createEmblaCarousel'
1+
export { UseEmblaCarouselType } from './components/useEmblaCarousel'
2+
export { default } from './components/useEmblaCarousel'

0 commit comments

Comments
 (0)