Skip to content

Commit 05c016a

Browse files
authored
Merge pull request #46 from vadimkorr/feature/45-promisify-functions
feature/45 Promisify functions
2 parents e9805e3 + d9fdd87 commit 05c016a

File tree

4 files changed

+117
-101
lines changed

4 files changed

+117
-101
lines changed

src/components/Carousel/Carousel.svelte

Lines changed: 68 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -13,30 +13,19 @@
1313
} from '../../utils/event'
1414
import { getAdjacentIndexes } from '../../utils/page'
1515
import { get } from '../../utils/object'
16-
import { ProgressManager } from '../../utils/ProgressManager.js'
16+
import { ProgressManager } from '../../utils/ProgressManager'
17+
import { wait } from '../../utils/interval'
1718
1819
const dispatch = createEventDispatcher()
1920
2021
const autoplayDirectionFnDescription = {
21-
[NEXT]: () => {
22-
progressManager.start(() => {
23-
showNextPage()
24-
})
25-
},
26-
[PREV]: () => {
27-
progressManager.start(() => {
28-
showPrevPage()
29-
})
30-
}
22+
[NEXT]: async () => await progressManager.start(showNextPage),
23+
[PREV]: async () => await progressManager.start(showPrevPage)
3124
}
3225
3326
const directionFnDescription = {
34-
[NEXT]: () => {
35-
showNextPage()
36-
},
37-
[PREV]: () => {
38-
showPrevPage()
39-
}
27+
[NEXT]: showNextPage,
28+
[PREV]: showPrevPage
4029
}
4130
4231
/**
@@ -70,6 +59,9 @@
7059
* Enables autoplay of pages
7160
*/
7261
export let autoplay = false
62+
$: {
63+
applyAutoplayIfNeeded(autoplay)
64+
}
7365
7466
/**
7567
* Autoplay change interval (ms)
@@ -96,31 +88,42 @@
9688
*/
9789
export let dots = true
9890
99-
export function goTo(pageIndex, options) {
91+
export async function goTo(pageIndex, options) {
10092
const animated = get(options, 'animated', true)
10193
if (typeof pageIndex !== 'number') {
10294
throw new Error('pageIndex should be a number')
10395
}
104-
showPage(pageIndex + Number(infinite), { animated })
96+
await showPage(pageIndex + Number(infinite), { animated })
10597
}
10698
107-
export function goToPrev(options) {
99+
export async function goToPrev(options) {
108100
const animated = get(options, 'animated', true)
109-
showPrevPage({ animated })
101+
await showPrevPage({ animated })
110102
}
111103
112-
export function goToNext(options) {
104+
export async function goToNext(options) {
113105
const animated = get(options, 'animated', true)
114-
showNextPage({ animated })
106+
await showNextPage({ animated })
115107
}
116108
117109
let store = createStore()
118110
let currentPageIndex = 0
119-
$: originalCurrentPageIndex = currentPageIndex - Number(infinite);
111+
$: originalCurrentPageIndex = getOriginalCurrentPageIndex(currentPageIndex, pagesCount, infinite) // index without cloenes
120112
$: dispatch('pageChange', originalCurrentPageIndex)
121113
122114
let pagesCount = 0
123115
$: originalPagesCount = Math.max(pagesCount - (infinite ? 2 : 0), 1) // without clones
116+
117+
function getOriginalCurrentPageIndex(currentPageIndex, pagesCount, infinite) {
118+
if (infinite) {
119+
const CLONES_COUNT = 2
120+
if (currentPageIndex === pagesCount - 1) return 0
121+
if (currentPageIndex === 0) return (pagesCount - CLONES_COUNT) - 1
122+
return currentPageIndex - 1
123+
}
124+
return currentPageIndex
125+
}
126+
124127
let pageWidth = 0
125128
let offset = 0
126129
let pageWindowElement
@@ -169,7 +172,7 @@
169172
pagesElement.append(first.cloneNode(true))
170173
}
171174
172-
function applyAutoplayIfNeeded(options) {
175+
async function applyAutoplayIfNeeded(autoplay) {
173176
// prevent progress change if not infinite for first and last page
174177
if (
175178
!infinite && (
@@ -180,16 +183,8 @@
180183
progressManager.reset()
181184
return
182185
}
183-
if (autoplay) {
184-
const delayMs = get(options, 'delayMs', 0)
185-
if (delayMs) {
186-
setTimeout(() => {
187-
autoplayDirectionFnDescription[autoplayDirection]()
188-
}, delayMs)
189-
} else {
190-
autoplayDirectionFnDescription[autoplayDirection]()
191-
}
192-
}
186+
187+
autoplay && await autoplayDirectionFnDescription[autoplayDirection]()
193188
}
194189
195190
let cleanupFns = []
@@ -211,8 +206,6 @@
211206
applyPageSizes()
212207
}
213208
214-
applyAutoplayIfNeeded()
215-
216209
addResizeEventListener(applyPageSizes)
217210
})()
218211
})
@@ -222,26 +215,30 @@
222215
cleanupFns.filter(fn => fn && typeof fn === 'function').forEach(fn => fn())
223216
})
224217
225-
function handlePageChange(pageIndex) {
226-
showPage(pageIndex + Number(infinite))
218+
async function handlePageChange(pageIndex) {
219+
await showPage(pageIndex + Number(infinite))
227220
}
228221
229222
function offsetPage(animated) {
230-
// _duration is an offset animation time
231-
_duration = animated ? duration : 0
232-
offset = -currentPageIndex * pageWidth
223+
return new Promise((resolve) => {
224+
// _duration is an offset animation time
225+
_duration = animated ? duration : 0
226+
offset = -currentPageIndex * pageWidth
227+
setTimeout(() => {
228+
resolve()
229+
}, _duration)
230+
})
233231
}
234232
235233
// makes delayed jump to 1st or last element
236-
function jumpIfNeeded() {
234+
async function jumpIfNeeded() {
237235
let jumped = false
238236
if (infinite) {
239237
if (currentPageIndex === 0) {
240-
// offsetDelayMs should depend on _duration, as it wait when offset finishes
241-
showPage(pagesCount - 2, { offsetDelayMs: _duration, animated: false })
238+
await showPage(pagesCount - 2, { animated: false })
242239
jumped = true
243240
} else if (currentPageIndex === pagesCount - 1) {
244-
showPage(1, { offsetDelayMs: _duration, animated: false })
241+
await showPage(1, { animated: false })
245242
jumped = true
246243
}
247244
}
@@ -250,54 +247,43 @@
250247
251248
// Disable page change while animation is in progress
252249
let disabled = false
253-
function safeChangePage(cb, options) {
254-
const animated = get(options, 'animated', true)
250+
async function changePage(updateStoreFn, options) {
255251
if (disabled) return
256-
cb()
257252
disabled = true
258-
setTimeout(() => {
259-
disabled = false
260-
}, animated ? duration : 0)
253+
254+
updateStoreFn()
255+
await offsetPage(get(options, 'animated', true))
256+
disabled = false
257+
258+
const jumped = await jumpIfNeeded()
259+
!jumped && applyAutoplayIfNeeded(autoplay) // no need to wait it finishes
261260
}
262261
263-
function showPage(pageIndex, options) {
264-
const animated = get(options, 'animated', true)
265-
const offsetDelayMs = get(options, 'offsetDelayMs', 0)
266-
safeChangePage(() => {
267-
store.moveToPage({ pageIndex, pagesCount })
268-
// delayed page transition, used for infinite autoplay to jump to real page
269-
setTimeout(() => {
270-
offsetPage(animated)
271-
const jumped = jumpIfNeeded()
272-
!jumped && applyAutoplayIfNeeded({ delayMs: _duration }) // while offset animation is in progress (delayMs = _duration ms) wait for it
273-
}, offsetDelayMs)
274-
}, { animated })
262+
async function showPage(pageIndex, options) {
263+
await changePage(
264+
() => store.moveToPage({ pageIndex, pagesCount }),
265+
options
266+
)
275267
}
276-
function showPrevPage(options) {
277-
const animated = get(options, 'animated', true)
278-
safeChangePage(() => {
279-
store.prev({ infinite, pagesCount })
280-
offsetPage(animated)
281-
const jumped = jumpIfNeeded()
282-
!jumped && applyAutoplayIfNeeded({ delayMs: _duration })
283-
}, { animated })
268+
async function showPrevPage(options) {
269+
await changePage(
270+
() => store.prev({ infinite, pagesCount }),
271+
options
272+
)
284273
}
285-
function showNextPage(options) {
286-
const animated = get(options, 'animated', true)
287-
safeChangePage(() => {
288-
store.next({ infinite, pagesCount })
289-
offsetPage(animated)
290-
const jumped = jumpIfNeeded()
291-
!jumped && applyAutoplayIfNeeded({ delayMs: _duration })
292-
}, { animated })
274+
async function showNextPage(options) {
275+
await changePage(
276+
() => store.next({ infinite, pagesCount }),
277+
options
278+
)
293279
}
294280
295281
// gestures
296282
function handleSwipeStart() {
297283
_duration = 0
298284
}
299-
function handleThreshold(event) {
300-
directionFnDescription[event.detail.direction]()
285+
async function handleThreshold(event) {
286+
await directionFnDescription[event.detail.direction]()
301287
}
302288
function handleSwipeMove(event) {
303289
offset += event.detail.dx

src/utils/ProgressManager.js

Lines changed: 25 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import { setIntervalImmediate } from './interval'
22

33
const STEP_MS = 35
4+
const MAX_VALUE = 1
5+
46
export class ProgressManager {
57
#autoplayDuration
68
#onProgressValueChange
@@ -17,25 +19,28 @@ export class ProgressManager {
1719
}
1820

1921
start(onFinish) {
20-
this.reset()
21-
22-
const stepMs = Math.min(STEP_MS, this.#autoplayDuration)
23-
let progress = -stepMs
24-
25-
this.#interval = setIntervalImmediate(() => {
26-
if (this.#paused) {
27-
return
28-
}
29-
progress += stepMs
30-
31-
const value = progress / this.#autoplayDuration
32-
this.#onProgressValueChange(value)
33-
34-
if (value > 1) {
35-
this.reset()
36-
onFinish()
37-
}
38-
}, stepMs)
22+
return new Promise((resolve) => {
23+
this.reset()
24+
25+
const stepMs = Math.min(STEP_MS, this.#autoplayDuration)
26+
let progress = -stepMs
27+
28+
this.#interval = setIntervalImmediate(async () => {
29+
if (this.#paused) {
30+
return
31+
}
32+
progress += stepMs
33+
34+
const value = progress / this.#autoplayDuration
35+
this.#onProgressValueChange(value)
36+
37+
if (value > MAX_VALUE) {
38+
this.reset()
39+
await onFinish()
40+
resolve()
41+
}
42+
}, stepMs)
43+
})
3944
}
4045

4146
pause() {
@@ -48,5 +53,6 @@ export class ProgressManager {
4853

4954
reset() {
5055
clearInterval(this.#interval)
56+
this.#onProgressValueChange(MAX_VALUE)
5157
}
5258
}

src/utils/interval.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,11 @@ export const setIntervalImmediate = (fn, ms) => {
22
fn();
33
return setInterval(fn, ms);
44
}
5+
6+
export const wait = (ms) => {
7+
return new Promise((resolve) => {
8+
setTimeout(() => {
9+
resolve()
10+
}, ms)
11+
})
12+
}

src/utils/interval.test.js

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import {
22
setIntervalImmediate,
3+
wait
34
} from './interval.js'
45

56
describe('setIntervalImmediate', () => {
@@ -28,3 +29,18 @@ describe('setIntervalImmediate', () => {
2829
expect(clearInterval).toHaveBeenCalledWith(interval)
2930
})
3031
})
32+
33+
describe('wait', () => {
34+
beforeEach(() => {
35+
jest.useFakeTimers();
36+
})
37+
38+
it('wait n ms', () => {
39+
const ms = 1000
40+
41+
wait(ms)
42+
jest.runAllTimers()
43+
44+
expect(setTimeout).toHaveBeenCalledWith(expect.any(Function), ms)
45+
})
46+
})

0 commit comments

Comments
 (0)