Skip to content

Commit

Permalink
feat: Support matchMedia in DomWatcher
Browse files Browse the repository at this point in the history
MediaQueryList instances can now be watched
for changes using DomWatcher.
  • Loading branch information
amacdonald-google committed Apr 27, 2022
1 parent 7d70000 commit ec9637c
Show file tree
Hide file tree
Showing 2 changed files with 67 additions and 5 deletions.
4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -40,13 +40,15 @@
],
"exclude": [
"_ava",
"_typedoc_theme",
"**/*.d.ts",
"docs/",
"examples/",
"coverage/",
"lib/",
"webpack/",
"src/playground"
"src/playground",
"server.js"
],
"reporter": [
"html",
Expand Down
68 changes: 64 additions & 4 deletions src/dom/dom-watcher.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
import * as bom from '../dom/bom';

type MediaQueryListListener = (
this: MediaQueryList,
ev: MediaQueryListEventMap[keyof MediaQueryListEventMap]
// eslint-disable-next-line @typescript-eslint/no-explicit-any
) => any;
type ResizeObserverListener = (a: ResizeObserverEntry[]) => void;
type MutationObserverListener = (a: MutationRecord[]) => void;

export interface DomWatcherConfig {
// https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener
// The default event listerner options including passive, once etc.
Expand All @@ -8,7 +16,7 @@ export interface DomWatcherConfig {
/**
* The element to Watch
*/
element: HTMLElement | Window | Document;
element: HTMLElement | Window | Document | MediaQueryList;

/**
* The name of the event to watch.
Expand Down Expand Up @@ -230,6 +238,42 @@ export interface DomWatcherConfig {
*
* ```
*
* #### MatchMedia
* https://developer.mozilla.org/en-US/docs/Web/API/Window/matchMedia
* DomWatcher supports MediaQueryLists that result from calls to matchMedia.
* That way DomWatcher can be configured to watch for changes across any/all
* breakpoints a site is designed around.
*
* ```ts
* const mediaQueries = new Map([
* [
* 'desktop',
* window.matchMedia('(min-width: 1440px)')],
* [
* 'laptop',
* window.matchMedia('(min-width: 1024px) and (max-width: 1439px)')],
* [
* 'tablet',
* window.matchMedia('(min-width: 600px) and (max-width: 1023px)')],
* [
* 'mobile',
* window.matchMedia('(max-width: 599px)')],
* ]);
* const watchBreakpoints = () => {
* Array.from(mediaQueries.entries())
* .forEach(([breakpoint, mediaQuery]) => {
* watcher.add({
* element: mediaQuery,
* on: 'change',
* callback: (e) => {
* if (e.matches) {
* // Handle the breakpoint
* }
* },
* });
* });
* }
* ```
*
*/
export class DomWatcher {
Expand Down Expand Up @@ -285,23 +329,39 @@ export class DomWatcher {
// Use the resizeObserver if we want to listen to resizing of DOM elements.
else if (config.on === 'resize' && config.element !== window) {
const resizeObserver = new ResizeObserver(entries => {
listener(entries);
(<ResizeObserverListener>listener)(entries);
});
resizeObserver.observe(config.element as HTMLElement);
config.remover = () => {
resizeObserver.unobserve(config.element as HTMLElement);
};
} else if (config.on === 'mutation') {
const mutationObserver = new MutationObserver(mutationsList => {
listener(mutationsList);
(<MutationObserverListener>listener)(mutationsList);
});
mutationObserver.observe(
config.element as HTMLElement,
config.eventOptions
<MutationObserverInit | undefined>config.eventOptions
);
config.remover = () => {
mutationObserver.disconnect();
};
} else if (config.element instanceof MediaQueryList) {
// Add listening.
config.element.addEventListener(
config.on as keyof MediaQueryListEventMap,
<MediaQueryListListener>listener,
<boolean | AddEventListenerOptions>config.eventOptions || {}
);

// Generate the remover.
config.remover = () => {
config.element.removeEventListener(
config.on as keyof MediaQueryListEventMap,
<MediaQueryListListener>listener,
<boolean | EventListenerOptions>config.eventOptions || {}
);
};
} else {
// Add listening.
config.element.addEventListener(
Expand Down

0 comments on commit ec9637c

Please sign in to comment.