Skip to content

Commit 7d5a47d

Browse files
committed
Implement #202.
1 parent 415a416 commit 7d5a47d

18 files changed

+331
-132
lines changed

packages/embla-carousel/src/components/Animations.ts

+9-7
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ export type AnimationsUpdateType = (engine: EngineType) => void
66
export type AnimationsRenderType = (engine: EngineType, alpha: number) => void
77

88
export type AnimationsType = {
9-
init: () => void
9+
init: (ownerWindow: WindowType) => void
1010
destroy: () => void
1111
start: () => void
1212
stop: () => void
@@ -15,19 +15,21 @@ export type AnimationsType = {
1515
}
1616

1717
export function Animations(
18-
ownerDocument: Document,
19-
ownerWindow: WindowType,
2018
update: () => void,
2119
render: (alpha: number) => void
2220
): AnimationsType {
2321
const documentVisibleHandler = EventStore()
2422
const fixedTimeStep = 1000 / 60
2523

24+
let windowInstance: WindowType
2625
let lastTimeStamp: number | null = null
2726
let accumulatedTime = 0
2827
let animationId = 0
2928

30-
function init(): void {
29+
function init(ownerWindow: WindowType): void {
30+
const ownerDocument = ownerWindow.document
31+
windowInstance = ownerWindow
32+
3133
documentVisibleHandler.add(ownerDocument, 'visibilitychange', () => {
3234
if (ownerDocument.hidden) reset()
3335
})
@@ -59,17 +61,17 @@ export function Animations(
5961
render(alpha)
6062

6163
if (animationId) {
62-
animationId = ownerWindow.requestAnimationFrame(animate)
64+
animationId = windowInstance.requestAnimationFrame(animate)
6365
}
6466
}
6567

6668
function start(): void {
6769
if (animationId) return
68-
animationId = ownerWindow.requestAnimationFrame(animate)
70+
animationId = windowInstance.requestAnimationFrame(animate)
6971
}
7072

7173
function stop(): void {
72-
ownerWindow.cancelAnimationFrame(animationId)
74+
windowInstance.cancelAnimationFrame(animationId)
7375
lastTimeStamp = null
7476
accumulatedTime = 0
7577
animationId = 0

packages/embla-carousel/src/components/Axis.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { NodeRectType } from './NodeRects'
1+
import { NodeRectType } from './NodeHandler'
22

33
export type AxisOptionType = 'x' | 'y'
44
export type AxisDirectionOptionType = 'ltr' | 'rtl'

packages/embla-carousel/src/components/DragHandler.ts

+9-4
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ import {
2121
} from './utils'
2222

2323
export type DragHandlerType = {
24-
init: () => void
24+
init: (windowInstance: WindowType) => void
2525
destroy: () => void
2626
pointerDown: () => boolean
2727
}
@@ -30,8 +30,6 @@ export function DragHandler(
3030
active: boolean,
3131
axis: AxisType,
3232
rootNode: HTMLElement,
33-
ownerDocument: Document,
34-
ownerWindow: WindowType,
3533
target: Vector1DType,
3634
dragTracker: DragTrackerType,
3735
location: Vector1DType,
@@ -58,6 +56,8 @@ export function DragHandler(
5856
const freeForceBoost = { mouse: 500, touch: 600 }
5957
const baseSpeed = dragFree ? 43 : 25
6058

59+
let ownerDocument: Document
60+
let ownerWindow: WindowType
6161
let isMoving = false
6262
let startScroll = 0
6363
let startCross = 0
@@ -66,9 +66,14 @@ export function DragHandler(
6666
let preventClick = false
6767
let isMouse = false
6868

69-
function init(): void {
69+
function init(windowInstance: WindowType): void {
7070
if (!active) return
7171

72+
ownerDocument = windowInstance.document
73+
ownerWindow = windowInstance
74+
75+
dragTracker.init(windowInstance)
76+
7277
const node = rootNode
7378
initEvents
7479
.add(node, 'dragstart', (evt) => evt.preventDefault(), nonPassiveEvent)

packages/embla-carousel/src/components/DragTracker.ts

+8-4
Original file line numberDiff line numberDiff line change
@@ -5,21 +5,24 @@ type PointerCoordType = keyof Touch | keyof MouseEvent
55
export type PointerEventType = TouchEvent | MouseEvent
66

77
export type DragTrackerType = {
8+
init: (windowInstance: WindowType) => void
89
pointerDown: (evt: PointerEventType) => number
910
pointerMove: (evt: PointerEventType) => number
1011
pointerUp: (evt: PointerEventType) => number
1112
readPoint: (evt: PointerEventType, evtAxis?: AxisOptionType) => number
1213
}
1314

14-
export function DragTracker(
15-
axis: AxisType,
16-
ownerWindow: WindowType
17-
): DragTrackerType {
15+
export function DragTracker(axis: AxisType): DragTrackerType {
1816
const logInterval = 170
1917

18+
let ownerWindow: WindowType
2019
let startEvent: PointerEventType
2120
let lastEvent: PointerEventType
2221

22+
function init(windowInstance: WindowType): void {
23+
ownerWindow = windowInstance
24+
}
25+
2326
function readTime(evt: PointerEventType): number {
2427
return evt.timeStamp
2528
}
@@ -57,6 +60,7 @@ export function DragTracker(
5760
}
5861

5962
const self: DragTrackerType = {
63+
init,
6064
pointerDown,
6165
pointerMove,
6266
pointerUp,

packages/embla-carousel/src/components/EmblaCarousel.ts

+52-34
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,13 @@ import { EventStore } from './EventStore'
33
import { WatchHandler, WatchHandlerType } from './WatchHandler'
44
import { EventHandler, EventHandlerType } from './EventHandler'
55
import { defaultOptions, EmblaOptionsType, OptionsType } from './Options'
6+
import { NodeHandler, NodeHandlerType } from './NodeHandler'
67
import { OptionsHandler } from './OptionsHandler'
78
import { PluginsHandler } from './PluginsHandler'
89
import { EmblaPluginsType, EmblaPluginType } from './Plugins'
9-
import { isNumber, isString, WindowType } from './utils'
1010
import { ScrollToDirectionType } from './ScrollTo'
11+
import { isNumber } from './utils'
12+
import { SsrHandler, SsrHandlerType } from './SsrHandler'
1113

1214
export type EmblaCarouselType = {
1315
canScrollNext: () => boolean
@@ -43,16 +45,17 @@ export type EmblaCarouselType = {
4345
jump?: boolean,
4446
direction?: ScrollToDirectionType
4547
) => void
48+
ssrStyles: () => string
4649
}
4750

4851
function EmblaCarousel(
4952
root: HTMLElement,
5053
userOptions?: EmblaOptionsType,
51-
userPlugins?: EmblaPluginType[]
54+
userPlugins?: EmblaPluginType[],
55+
headless?: boolean
5256
): EmblaCarouselType {
53-
const ownerDocument = root.ownerDocument
54-
const ownerWindow = <WindowType>ownerDocument.defaultView
55-
const optionsHandler = OptionsHandler(ownerWindow)
57+
const isSsr = !!headless
58+
const optionsHandler = OptionsHandler()
5659
const pluginsHandler = PluginsHandler(optionsHandler)
5760
const mediaHandlers = EventStore()
5861
const watchHandler = WatchHandler()
@@ -64,43 +67,33 @@ function EmblaCarousel(
6467

6568
let destroyed = false
6669
let engine: EngineType
70+
let nodeHandler: NodeHandlerType
71+
let ssrHandler: SsrHandlerType
6772
let optionsBase = mergeOptions(defaultOptions, EmblaCarousel.globalOptions)
6873
let options = mergeOptions(optionsBase)
6974
let pluginList: EmblaPluginType[] = []
7075
let pluginApis: EmblaPluginsType
71-
7276
let container: HTMLElement
7377
let slides: HTMLElement[]
7478

75-
function storeElements(): void {
76-
const { container: userContainer, slides: userSlides } = options
77-
78-
const customContainer = isString(userContainer)
79-
? root.querySelector(userContainer)
80-
: userContainer
81-
container = <HTMLElement>(customContainer || root.children[0])
82-
83-
const customSlides = isString(userSlides)
84-
? container.querySelectorAll(userSlides)
85-
: userSlides
86-
slides = <HTMLElement[]>[].slice.call(customSlides || container.children)
87-
}
88-
89-
function createEngine(options: OptionsType): EngineType {
79+
function createEngine(
80+
options: OptionsType,
81+
container: HTMLElement,
82+
slides: HTMLElement[]
83+
): EngineType {
9084
const engine = Engine(
9185
root,
9286
container,
9387
slides,
94-
ownerDocument,
95-
ownerWindow,
9688
options,
89+
nodeHandler,
9790
eventHandler,
9891
watchHandler
9992
)
10093

10194
if (options.loop && !engine.slideLooper.canLoop()) {
10295
const optionsWithoutLoop = Object.assign({}, options, { loop: false })
103-
return createEngine(optionsWithoutLoop)
96+
return createEngine(optionsWithoutLoop, container, slides)
10497
}
10598
return engine
10699
}
@@ -111,32 +104,51 @@ function EmblaCarousel(
111104
): void {
112105
if (destroyed) return
113106

107+
nodeHandler = NodeHandler(root, isSsr)
108+
const { ownerWindow } = nodeHandler
109+
110+
optionsHandler.init(ownerWindow)
114111
optionsBase = mergeOptions(optionsBase, withOptions)
115112
options = optionsAtMedia(optionsBase)
116113
pluginList = withPlugins || pluginList
117114

118-
storeElements()
115+
const nodes = nodeHandler.getNodes(options)
116+
container = nodes.container
117+
slides = nodes.slides
118+
engine = createEngine(options, container, slides)
119119

120-
engine = createEngine(options)
120+
ssrHandler = SsrHandler(
121+
isSsr,
122+
container,
123+
engine.axis,
124+
nodeHandler,
125+
optionsBase,
126+
mergeOptions,
127+
createEngine
128+
)
121129

122130
optionsMediaQueries([
123131
optionsBase,
124132
...pluginList.map(({ options }) => options)
125133
]).forEach((query) => mediaHandlers.add(query, 'change', reActivate))
126134

127135
if (!options.active) return
136+
if (!ownerWindow) return
137+
if (isSsr) return
128138

129139
engine.translate.to(engine.location.get())
130-
engine.animation.init()
131-
engine.slidesInView.init()
132-
engine.slideFocus.init()
133-
engine.resizeHandler.init()
134-
engine.slidesHandler.init()
140+
engine.animation.init(ownerWindow)
141+
engine.resizeHandler.init(ownerWindow)
142+
engine.slidesInView.init(ownerWindow)
143+
engine.slidesHandler.init(ownerWindow)
135144
engine.eventHandler.init(self)
136145
engine.watchHandler.init(self)
146+
engine.slideFocus.init()
137147

138148
if (engine.options.loop) engine.slideLooper.loop()
139-
if (container.offsetParent && slides.length) engine.dragHandler.init()
149+
if (container.offsetParent && slides.length) {
150+
engine.dragHandler.init(ownerWindow)
151+
}
140152

141153
pluginApis = pluginsHandler.init(self, pluginList)
142154
}
@@ -180,6 +192,7 @@ function EmblaCarousel(
180192
direction?: ScrollToDirectionType
181193
): void {
182194
if (!options.active || destroyed) return
195+
183196
engine.scrollBody
184197
.useBaseFriction()
185198
.useDuration(jump === true ? 0 : options.duration)
@@ -245,6 +258,10 @@ function EmblaCarousel(
245258
return pluginApis
246259
}
247260

261+
function ssrStyles(): string {
262+
return ssrHandler.getStyles()
263+
}
264+
248265
function internalEngine(): EngineType {
249266
return engine
250267
}
@@ -286,11 +303,12 @@ function EmblaCarousel(
286303
slideNodes,
287304
slidesInView,
288305
slidesNotInView,
289-
snapList
306+
snapList,
307+
ssrStyles
290308
}
291309

292310
activate(userOptions, userPlugins)
293-
setTimeout(() => eventHandler.emit('init', null), 0)
311+
setTimeout(() => eventHandler.emit('init', null), 0) // TODO: Won't work in SSR
294312
return self
295313
}
296314

0 commit comments

Comments
 (0)