-
-
Notifications
You must be signed in to change notification settings - Fork 4.3k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: add
svelte/reactivity/window
module (#14660)
* feat: add `svelte/reactivity/window` module * lint * fix * hide private types * online binding * tweak docs * tweak * add @SInCE tags --------- Co-authored-by: Simon Holthausen <simon.holthausen@vercel.com>
- Loading branch information
1 parent
a2539cf
commit d43a10b
Showing
9 changed files
with
289 additions
and
23 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
--- | ||
'svelte': minor | ||
--- | ||
|
||
feat: add `svelte/reactivity/window` module |
15 changes: 15 additions & 0 deletions
15
documentation/docs/98-reference/21-svelte-reactivity-window.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
--- | ||
title: svelte/reactivity/window | ||
--- | ||
|
||
This module exports reactive versions of various `window` values, each of which has a reactive `current` property that you can reference in reactive contexts (templates, [deriveds]($derived) and [effects]($effect)) without using [`<svelte:window>`](svelte-window) bindings or manually creating your own event listeners. | ||
|
||
```svelte | ||
<script> | ||
import { innerWidth, innerHeight } from 'svelte/reactivity/window'; | ||
</script> | ||
<p>{innerWidth.current}x{innerHeight.current}</p> | ||
``` | ||
|
||
> MODULE: svelte/reactivity/window |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
import { createSubscriber } from './create-subscriber.js'; | ||
|
||
/** | ||
* @template T | ||
*/ | ||
export class ReactiveValue { | ||
#fn; | ||
#subscribe; | ||
|
||
/** | ||
* | ||
* @param {() => T} fn | ||
* @param {(update: () => void) => void} onsubscribe | ||
*/ | ||
constructor(fn, onsubscribe) { | ||
this.#fn = fn; | ||
this.#subscribe = createSubscriber(onsubscribe); | ||
} | ||
|
||
get current() { | ||
this.#subscribe(); | ||
return this.#fn(); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,154 @@ | ||
import { BROWSER } from 'esm-env'; | ||
import { on } from '../../events/index.js'; | ||
import { ReactiveValue } from '../reactive-value.js'; | ||
import { get } from '../../internal/client/index.js'; | ||
import { set, source } from '../../internal/client/reactivity/sources.js'; | ||
|
||
/** | ||
* `scrollX.current` is a reactive view of `window.scrollX`. On the server it is `undefined`. | ||
* @since 5.11.0 | ||
*/ | ||
export const scrollX = new ReactiveValue( | ||
BROWSER ? () => window.scrollX : () => undefined, | ||
(update) => on(window, 'scroll', update) | ||
); | ||
|
||
/** | ||
* `scrollY.current` is a reactive view of `window.scrollY`. On the server it is `undefined`. | ||
* @since 5.11.0 | ||
*/ | ||
export const scrollY = new ReactiveValue( | ||
BROWSER ? () => window.scrollY : () => undefined, | ||
(update) => on(window, 'scroll', update) | ||
); | ||
|
||
/** | ||
* `innerWidth.current` is a reactive view of `window.innerWidth`. On the server it is `undefined`. | ||
* @since 5.11.0 | ||
*/ | ||
export const innerWidth = new ReactiveValue( | ||
BROWSER ? () => window.innerWidth : () => undefined, | ||
(update) => on(window, 'resize', update) | ||
); | ||
|
||
/** | ||
* `innerHeight.current` is a reactive view of `window.innerHeight`. On the server it is `undefined`. | ||
* @since 5.11.0 | ||
*/ | ||
export const innerHeight = new ReactiveValue( | ||
BROWSER ? () => window.innerHeight : () => undefined, | ||
(update) => on(window, 'resize', update) | ||
); | ||
|
||
/** | ||
* `outerWidth.current` is a reactive view of `window.outerWidth`. On the server it is `undefined`. | ||
* @since 5.11.0 | ||
*/ | ||
export const outerWidth = new ReactiveValue( | ||
BROWSER ? () => window.outerWidth : () => undefined, | ||
(update) => on(window, 'resize', update) | ||
); | ||
|
||
/** | ||
* `outerHeight.current` is a reactive view of `window.outerHeight`. On the server it is `undefined`. | ||
* @since 5.11.0 | ||
*/ | ||
export const outerHeight = new ReactiveValue( | ||
BROWSER ? () => window.outerHeight : () => undefined, | ||
(update) => on(window, 'resize', update) | ||
); | ||
|
||
/** | ||
* `screenLeft.current` is a reactive view of `window.screenLeft`. It is updated inside a `requestAnimationFrame` callback. On the server it is `undefined`. | ||
* @since 5.11.0 | ||
*/ | ||
export const screenLeft = new ReactiveValue( | ||
BROWSER ? () => window.screenLeft : () => undefined, | ||
(update) => { | ||
let value = window.screenLeft; | ||
|
||
let frame = requestAnimationFrame(function check() { | ||
frame = requestAnimationFrame(check); | ||
|
||
if (value !== (value = window.screenLeft)) { | ||
update(); | ||
} | ||
}); | ||
|
||
return () => { | ||
cancelAnimationFrame(frame); | ||
}; | ||
} | ||
); | ||
|
||
/** | ||
* `screenTop.current` is a reactive view of `window.screenTop`. It is updated inside a `requestAnimationFrame` callback. On the server it is `undefined`. | ||
* @since 5.11.0 | ||
*/ | ||
export const screenTop = new ReactiveValue( | ||
BROWSER ? () => window.screenTop : () => undefined, | ||
(update) => { | ||
let value = window.screenTop; | ||
|
||
let frame = requestAnimationFrame(function check() { | ||
frame = requestAnimationFrame(check); | ||
|
||
if (value !== (value = window.screenTop)) { | ||
update(); | ||
} | ||
}); | ||
|
||
return () => { | ||
cancelAnimationFrame(frame); | ||
}; | ||
} | ||
); | ||
|
||
/** | ||
* `online.current` is a reactive view of `navigator.onLine`. On the server it is `undefined`. | ||
* @since 5.11.0 | ||
*/ | ||
export const online = new ReactiveValue( | ||
BROWSER ? () => navigator.onLine : () => undefined, | ||
(update) => { | ||
const unsub_online = on(window, 'online', update); | ||
const unsub_offline = on(window, 'offline', update); | ||
return () => { | ||
unsub_online(); | ||
unsub_offline(); | ||
}; | ||
} | ||
); | ||
|
||
/** | ||
* `devicePixelRatio.current` is a reactive view of `window.devicePixelRatio`. On the server it is `undefined`. | ||
* Note that behaviour differs between browsers — on Chrome it will respond to the current zoom level, | ||
* on Firefox and Safari it won't. | ||
* @type {{ get current(): number }} | ||
* @since 5.11.0 | ||
*/ | ||
export const devicePixelRatio = /* @__PURE__ */ new (class DevicePixelRatio { | ||
#dpr = source(BROWSER ? window.devicePixelRatio : undefined); | ||
|
||
#update() { | ||
const off = on( | ||
window.matchMedia(`(resolution: ${window.devicePixelRatio}dppx)`), | ||
'change', | ||
() => { | ||
set(this.#dpr, window.devicePixelRatio); | ||
|
||
off(); | ||
this.#update(); | ||
} | ||
); | ||
} | ||
|
||
constructor() { | ||
this.#update(); | ||
} | ||
|
||
get current() { | ||
get(this.#dpr); | ||
return window.devicePixelRatio; | ||
} | ||
})(); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters