Skip to content

Commit

Permalink
feat: provide MediaQuery / prefersReducedMotion
Browse files Browse the repository at this point in the history
closes #5346
  • Loading branch information
dummdidumm committed Nov 25, 2024
1 parent 37e6c7f commit c5c4bea
Show file tree
Hide file tree
Showing 5 changed files with 76 additions and 0 deletions.
8 changes: 8 additions & 0 deletions packages/svelte/src/motion/index.js
Original file line number Diff line number Diff line change
@@ -1,2 +1,10 @@
import { MediaQuery } from 'svelte/reactivity';

export * from './spring.js';
export * from './tweened.js';

/**
* A media query that matches if the user has requested reduced motion.
* @type {MediaQuery}
*/
export const prefersReducedMotion = new MediaQuery('(prefers-reduced-motion: reduce)');
1 change: 1 addition & 0 deletions packages/svelte/src/reactivity/index-client.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@ export { SvelteSet } from './set.js';
export { SvelteMap } from './map.js';
export { SvelteURL } from './url.js';
export { SvelteURLSearchParams } from './url-search-params.js';
export { MediaQuery } from './media-query.js';
4 changes: 4 additions & 0 deletions packages/svelte/src/reactivity/index-server.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,7 @@ export const SvelteSet = globalThis.Set;
export const SvelteMap = globalThis.Map;
export const SvelteURL = globalThis.URL;
export const SvelteURLSearchParams = globalThis.URLSearchParams;

export class MediaQuery {
matches = false;
}
49 changes: 49 additions & 0 deletions packages/svelte/src/reactivity/media-query.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import { get, tick } from '../internal/client/runtime.js';
import { set, source } from '../internal/client/reactivity/sources.js';
import { effect_tracking, render_effect } from '../internal/client/reactivity/effects.js';

/**
* Creates a media query and provides a `matches` property that reflects its current state.
*/
export class MediaQuery {
#matches = source(false);
#subscribers = 0;
#query;
/** @type {any} */
#listener;

get matches() {
if (effect_tracking()) {
render_effect(() => {
if (this.#subscribers === 0) {
this.#listener = () => set(this.#matches, this.#query.matches);
this.#query.addEventListener('change', this.#listener);
}

this.#subscribers += 1;

return () => {
tick().then(() => {
// Only count down after timeout, else we would reach 0 before our own render effect reruns,
// but reach 1 again when the tick callback of the prior teardown runs. That would mean we
// re-subcribe unnecessarily and create a memory leak because the old subscription is never cleaned up.
this.#subscribers -= 1;

if (this.#subscribers === 0) {
this.#query.removeEventListener('change', this.#listener);
}
});
};
});
}

return get(this.#matches);
}

/** @param {string} query */
constructor(query) {
this.#query = window.matchMedia(query);
console.log('MediaQuery.constructor', query, this.#query);

Check failure on line 46 in packages/svelte/src/reactivity/media-query.js

View workflow job for this annotation

GitHub Actions / Lint

Unexpected console statement
this.#matches.v = this.#query.matches;
}
}
14 changes: 14 additions & 0 deletions packages/svelte/types/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1630,6 +1630,7 @@ declare module 'svelte/legacy' {
}

declare module 'svelte/motion' {
import type { MediaQuery } from 'svelte/reactivity';
export interface Spring<T> extends Readable<T> {
set: (new_value: T, opts?: SpringUpdateOpts) => Promise<void>;
update: (fn: Updater<T>, opts?: SpringUpdateOpts) => Promise<void>;
Expand Down Expand Up @@ -1676,6 +1677,10 @@ declare module 'svelte/motion' {
easing?: (t: number) => number;
interpolate?: (a: T, b: T) => (t: number) => T;
}
/**
* A media query that matches if the user has requested reduced motion.
* */
export const prefersReducedMotion: MediaQuery;
/**
* The spring function in Svelte creates a store whose value is animated, with a motion that simulates the behavior of a spring. This means when the value changes, instead of transitioning at a steady rate, it "bounces" like a spring would, depending on the physics parameters provided. This adds a level of realism to the transitions and can enhance the user experience.
*
Expand Down Expand Up @@ -1720,6 +1725,15 @@ declare module 'svelte/reactivity' {
[REPLACE](params: URLSearchParams): void;
#private;
}
/**
* Creates a media query and provides a `matches` property that reflects its current state.
*/
export class MediaQuery {

constructor(query: string);
get matches(): boolean;
#private;
}

export {};
}
Expand Down

0 comments on commit c5c4bea

Please sign in to comment.