Skip to content

Commit 66aad3c

Browse files
authored
feat: support configurable scheduler for timed composables (#5129)
1 parent 2392131 commit 66aad3c

File tree

15 files changed

+286
-100
lines changed

15 files changed

+286
-100
lines changed

packages/core/_configurable.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import type { Fn, Pausable } from '@vueuse/shared'
12
import { isClient } from '@vueuse/shared'
23

34
export interface ConfigurableWindow {
@@ -48,3 +49,10 @@ export interface ConfigurableDeepRefs<D extends boolean> {
4849
*/
4950
deepRefs?: D
5051
}
52+
53+
export interface ConfigurableScheduler {
54+
/**
55+
* Custom scheduler to use for interval execution.
56+
*/
57+
scheduler?: (cb: Fn) => Pausable
58+
}

packages/core/useCountdown/index.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ category: Time
44

55
# useCountdown
66

7-
Wrapper for `useIntervalFn` that provides a countdown timer.
7+
Reactive countdown timer in seconds.
88

99
## Usage
1010

packages/core/useCountdown/index.ts

Lines changed: 38 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,27 @@
1-
import type { Pausable } from '@vueuse/shared'
1+
import type { AnyFn, Pausable } from '@vueuse/shared'
22
import type { MaybeRefOrGetter, ShallowRef } from 'vue'
3+
import type { ConfigurableScheduler } from '../_configurable'
34
import { useIntervalFn } from '@vueuse/shared'
45
import { shallowRef, toValue } from 'vue'
56

6-
export interface UseCountdownOptions {
7+
function getDefaultScheduler(options: UseCountdownOptions) {
8+
if ('interval' in options || 'immediate' in options) {
9+
const {
10+
interval = 1000,
11+
immediate = false,
12+
} = options
13+
14+
return (cb: AnyFn) => useIntervalFn(cb, interval, { immediate })
15+
}
16+
17+
return (cb: AnyFn) => useIntervalFn(cb, 1000, { immediate: false })
18+
}
19+
20+
export interface UseCountdownOptions extends ConfigurableScheduler {
721
/**
822
* Interval for the countdown in milliseconds. Default is 1000ms.
23+
*
24+
* @deprecated Please use `scheduler` option instead
925
*/
1026
interval?: MaybeRefOrGetter<number>
1127
/**
@@ -19,6 +35,7 @@ export interface UseCountdownOptions {
1935
/**
2036
* Start the countdown immediately
2137
*
38+
* @deprecated Please use `scheduler` option instead
2239
* @default false
2340
*/
2441
immediate?: boolean
@@ -44,55 +61,61 @@ export interface UseCountdownReturn extends Pausable {
4461
}
4562

4663
/**
47-
* Wrapper for `useIntervalFn` that provides a countdown timer in seconds.
64+
* Reactive countdown timer in seconds.
4865
*
4966
* @param initialCountdown
5067
* @param options
5168
*
5269
* @see https://vueuse.org/useCountdown
5370
*/
54-
export function useCountdown(initialCountdown: MaybeRefOrGetter<number>, options?: UseCountdownOptions): UseCountdownReturn {
71+
export function useCountdown(initialCountdown: MaybeRefOrGetter<number>, options: UseCountdownOptions = {}): UseCountdownReturn {
5572
const remaining = shallowRef(toValue(initialCountdown))
5673

57-
const intervalController = useIntervalFn(() => {
74+
const {
75+
scheduler = getDefaultScheduler(options),
76+
onTick,
77+
onComplete,
78+
} = options
79+
80+
const controls = scheduler(() => {
5881
const value = remaining.value - 1
5982
remaining.value = value < 0 ? 0 : value
60-
options?.onTick?.()
83+
onTick?.()
6184
if (remaining.value <= 0) {
62-
intervalController.pause()
63-
options?.onComplete?.()
85+
controls.pause()
86+
onComplete?.()
6487
}
65-
}, options?.interval ?? 1000, { immediate: options?.immediate ?? false })
88+
})
6689

6790
const reset = (countdown?: MaybeRefOrGetter<number>) => {
6891
remaining.value = toValue(countdown) ?? toValue(initialCountdown)
6992
}
7093

7194
const stop = () => {
72-
intervalController.pause()
95+
controls.pause()
7396
reset()
7497
}
7598

7699
const resume = () => {
77-
if (!intervalController.isActive.value) {
100+
if (!controls.isActive.value) {
78101
if (remaining.value > 0) {
79-
intervalController.resume()
102+
controls.resume()
80103
}
81104
}
82105
}
83106

84107
const start = (countdown?: MaybeRefOrGetter<number>) => {
85108
reset(countdown)
86-
intervalController.resume()
109+
controls.resume()
87110
}
88111

89112
return {
90113
remaining,
91114
reset,
92115
stop,
93116
start,
94-
pause: intervalController.pause,
117+
pause: controls.pause,
95118
resume,
96-
isActive: intervalController.isActive,
119+
isActive: controls.isActive,
97120
}
98121
}

packages/core/useElementByPoint/index.ts

Lines changed: 23 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,34 @@
1-
import type { Pausable } from '@vueuse/shared'
1+
import type { AnyFn, Pausable } from '@vueuse/shared'
22
import type { ComputedRef, MaybeRefOrGetter, ShallowRef } from 'vue'
3-
import type { ConfigurableDocument } from '../_configurable'
3+
import type { ConfigurableDocument, ConfigurableScheduler } from '../_configurable'
44
import { useIntervalFn } from '@vueuse/shared'
55
import { shallowRef, toValue } from 'vue'
66
import { defaultDocument } from '../_configurable'
77
import { useRafFn } from '../useRafFn'
88
import { useSupported } from '../useSupported'
99

10-
export interface UseElementByPointOptions<Multiple extends boolean = false> extends ConfigurableDocument {
10+
function getDefaultScheduler(options: UseElementByPointOptions<boolean>) {
11+
if ('interval' in options || 'immediate' in options) {
12+
const {
13+
interval = 'requestAnimationFrame',
14+
immediate = true,
15+
} = options
16+
17+
return interval === 'requestAnimationFrame'
18+
? (cb: AnyFn) => useRafFn(cb, { immediate })
19+
: (cb: AnyFn) => useIntervalFn(cb, interval, { immediate })
20+
}
21+
22+
return useRafFn
23+
}
24+
25+
export interface UseElementByPointOptions<Multiple extends boolean = false> extends ConfigurableDocument, ConfigurableScheduler {
1126
x: MaybeRefOrGetter<number>
1227
y: MaybeRefOrGetter<number>
1328
multiple?: MaybeRefOrGetter<Multiple>
29+
/** @deprecated Please use `scheduler` option instead */
1430
immediate?: boolean
31+
/** @deprecated Please use `scheduler` option instead */
1532
interval?: 'requestAnimationFrame' | number
1633
}
1734

@@ -32,8 +49,7 @@ export function useElementByPoint<M extends boolean = false>(options: UseElement
3249
y,
3350
document = defaultDocument,
3451
multiple,
35-
interval = 'requestAnimationFrame',
36-
immediate = true,
52+
scheduler = getDefaultScheduler(options),
3753
} = options
3854

3955
const isSupported = useSupported(() => {
@@ -45,15 +61,11 @@ export function useElementByPoint<M extends boolean = false>(options: UseElement
4561

4662
const element = shallowRef<any>(null)
4763

48-
const cb = () => {
64+
const controls = scheduler(() => {
4965
element.value = toValue(multiple)
5066
? document?.elementsFromPoint(toValue(x), toValue(y)) ?? []
5167
: document?.elementFromPoint(toValue(x), toValue(y)) ?? null
52-
}
53-
54-
const controls: Pausable = interval === 'requestAnimationFrame'
55-
? useRafFn(cb, { immediate })
56-
: useIntervalFn(cb, interval, { immediate })
68+
})
5769

5870
return {
5971
isSupported,

packages/core/useMemory/index.ts

Lines changed: 50 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,27 @@
1-
import type { UseIntervalFnOptions } from '@vueuse/shared'
1+
import type { AnyFn } from '@vueuse/shared'
2+
import type { ComputedRef, Ref } from 'vue'
3+
import type { ConfigurableScheduler } from '../_configurable'
24
import { useIntervalFn } from '@vueuse/shared'
35
import { ref as deepRef } from 'vue'
46
import { useSupported } from '../useSupported'
57

8+
function getDefaultScheduler(options: UseMemoryOptions) {
9+
if ('interval' in options || 'immediate' in options || 'immediateCallback' in options) {
10+
const {
11+
interval = 1000,
12+
immediate,
13+
immediateCallback,
14+
} = options
15+
16+
return (cb: AnyFn) => useIntervalFn(cb, interval, {
17+
immediate,
18+
immediateCallback,
19+
})
20+
}
21+
22+
return useIntervalFn
23+
}
24+
625
/**
726
* Performance.memory
827
*
@@ -25,10 +44,32 @@ export interface MemoryInfo {
2544
[Symbol.toStringTag]: 'MemoryInfo'
2645
}
2746

28-
export interface UseMemoryOptions extends UseIntervalFnOptions {
47+
export interface UseMemoryOptions extends ConfigurableScheduler {
48+
/**
49+
* Start the timer immediately
50+
*
51+
* @deprecated Please use `scheduler` option instead
52+
* @default true
53+
*/
54+
immediate?: boolean
55+
56+
/**
57+
* Execute the callback immediately after calling `resume`
58+
*
59+
* @deprecated Please use `scheduler` option instead
60+
* @default false
61+
*/
62+
immediateCallback?: boolean
63+
64+
/** @deprecated Please use `scheduler` option instead */
2965
interval?: number
3066
}
3167

68+
export interface UseMemoryReturn {
69+
isSupported: ComputedRef<boolean>
70+
memory: Ref<MemoryInfo | undefined>
71+
}
72+
3273
type PerformanceMemory = Performance & {
3374
memory: MemoryInfo
3475
}
@@ -41,18 +82,19 @@ type PerformanceMemory = Performance & {
4182
*
4283
* @__NO_SIDE_EFFECTS__
4384
*/
44-
export function useMemory(options: UseMemoryOptions = {}) {
85+
export function useMemory(options: UseMemoryOptions = {}): UseMemoryReturn {
4586
const memory = deepRef<MemoryInfo>()
4687
const isSupported = useSupported(() => typeof performance !== 'undefined' && 'memory' in performance)
4788

4889
if (isSupported.value) {
49-
const { interval = 1000 } = options
50-
useIntervalFn(() => {
90+
const {
91+
scheduler = getDefaultScheduler,
92+
} = options
93+
94+
scheduler(() => {
5195
memory.value = (performance as PerformanceMemory).memory
52-
}, interval, { immediate: options.immediate, immediateCallback: options.immediateCallback })
96+
})
5397
}
5498

5599
return { isSupported, memory }
56100
}
57-
58-
export type UseMemoryReturn = ReturnType<typeof useMemory>

packages/core/useNow/component.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ export const UseNow = /* #__PURE__ */ defineComponent<
2525
{
2626
name: 'UseNow',
2727
props: [
28+
'scheduler',
2829
'immediate',
2930
'interval',
3031
],

packages/core/useNow/index.ts

Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,26 @@
1-
import type { Pausable } from '@vueuse/shared'
1+
import type { AnyFn, Pausable } from '@vueuse/shared'
22
import type { Ref } from 'vue'
3+
import type { ConfigurableScheduler } from '../_configurable'
34
import { useIntervalFn } from '@vueuse/shared'
45
import { ref as deepRef } from 'vue'
56
import { useRafFn } from '../useRafFn'
67

7-
export interface UseNowOptions<Controls extends boolean> {
8+
function getDefaultScheduler(options: UseNowOptions<boolean>) {
9+
if ('interval' in options || 'immediate' in options) {
10+
const {
11+
interval = 'requestAnimationFrame',
12+
immediate = true,
13+
} = options
14+
15+
return interval === 'requestAnimationFrame'
16+
? (fn: AnyFn) => useRafFn(fn, { immediate })
17+
: (fn: AnyFn) => useIntervalFn(fn, interval, options)
18+
}
19+
20+
return useRafFn
21+
}
22+
23+
export interface UseNowOptions<Controls extends boolean> extends ConfigurableScheduler {
824
/**
925
* Expose more controls
1026
*
@@ -15,13 +31,15 @@ export interface UseNowOptions<Controls extends boolean> {
1531
/**
1632
* Start the clock immediately
1733
*
34+
* @deprecated Please use `scheduler` option instead
1835
* @default true
1936
*/
2037
immediate?: boolean
2138

2239
/**
2340
* Update interval in milliseconds, or use requestAnimationFrame
2441
*
42+
* @deprecated Please use `scheduler` option instead
2543
* @default requestAnimationFrame
2644
*/
2745
interval?: 'requestAnimationFrame' | number
@@ -49,17 +67,14 @@ export function useNow(options: UseNowOptions<true>): { now: Ref<Date> } & Pausa
4967
export function useNow(options: UseNowOptions<boolean> = {}) {
5068
const {
5169
controls: exposeControls = false,
52-
interval = 'requestAnimationFrame',
53-
immediate = true,
70+
scheduler = getDefaultScheduler(options),
5471
} = options
5572

5673
const now = deepRef(new Date())
5774

5875
const update = () => now.value = new Date()
5976

60-
const controls: Pausable = interval === 'requestAnimationFrame'
61-
? useRafFn(update, { immediate })
62-
: useIntervalFn(update, interval, { immediate })
77+
const controls = scheduler(update)
6378

6479
if (exposeControls) {
6580
return {

0 commit comments

Comments
 (0)