Skip to content

Commit

Permalink
feat: allow media query to be initialized in a module (#68)
Browse files Browse the repository at this point in the history
  • Loading branch information
paoloricciuti authored Jun 6, 2024
1 parent 2320a91 commit f6ca05d
Show file tree
Hide file tree
Showing 2 changed files with 37 additions and 10 deletions.
5 changes: 5 additions & 0 deletions .changeset/slimy-weeks-yawn.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"runed": minor
---

feat: allow media query to be initialized in a module
42 changes: 32 additions & 10 deletions packages/runed/src/lib/utilities/MediaQuery/MediaQuery.svelte.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { extract } from "../extract/extract.js";
import { useEventListener } from "../useEventListener/useEventListener.svelte.js";
import type { MaybeGetter } from "$lib/internal/types.js";
import type { Getter, MaybeGetter } from "$lib/internal/types.js";
import { isBrowser } from "$lib/internal/utils/browser.js";

/**
* Take a media query (or a function that returns one if you want reactivity)
Expand Down Expand Up @@ -35,20 +35,42 @@ import type { MaybeGetter } from "$lib/internal/types.js";
*/
export class MediaQuery {
#matches: boolean | undefined = $state();
#effectRegistered = false;
#query_fn: Getter<string | void> = () => {};
// this will be initialized in the constructor
#query = $derived(this.#query_fn?.()) as string;

constructor(query: MaybeGetter<string>) {
$effect(() => {
const result = window.matchMedia(extract(query));

this.#matches = result.matches;
this.#query_fn = typeof query === "function" ? query : () => query;
}

useEventListener(result, "change", (changed) => {
this.#matches = changed.matches;
});
});
#matchMedia() {
const result = window.matchMedia(this.#query);
this.#matches = result.matches;
return result;
}

get matches(): boolean | undefined {
// if we are in an effect and this effect has not been registered yet
// we match the current value, register the listener and return match
if ($effect.active() && !this.#effectRegistered) {
$effect(() => {
const result = this.#matchMedia();
this.#effectRegistered = true;
useEventListener(result, "change", (changed) => {
this.#matches = changed.matches;
});
return () => {
this.#effectRegistered = false;
};
});
} else if (!$effect.active()) {
// otherwise if we are not in an effect and the effect has not
//been registered we just match media to get the current value
if (isBrowser()) {
this.#matchMedia();
}
}
return this.#matches;
}
}

0 comments on commit f6ca05d

Please sign in to comment.