diff --git a/package.json b/package.json index cb171400c4..747adec943 100644 --- a/package.json +++ b/package.json @@ -40,13 +40,15 @@ ], "exclude": [ "_ava", + "_typedoc_theme", "**/*.d.ts", "docs/", "examples/", "coverage/", "lib/", "webpack/", - "src/playground" + "src/playground", + "server.js" ], "reporter": [ "html", diff --git a/src/dom/dom-watcher.ts b/src/dom/dom-watcher.ts index 3b1968cd05..9906600894 100644 --- a/src/dom/dom-watcher.ts +++ b/src/dom/dom-watcher.ts @@ -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. @@ -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. @@ -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 { @@ -285,7 +329,7 @@ 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); + (listener)(entries); }); resizeObserver.observe(config.element as HTMLElement); config.remover = () => { @@ -293,15 +337,31 @@ export class DomWatcher { }; } else if (config.on === 'mutation') { const mutationObserver = new MutationObserver(mutationsList => { - listener(mutationsList); + (listener)(mutationsList); }); mutationObserver.observe( config.element as HTMLElement, - config.eventOptions + config.eventOptions ); config.remover = () => { mutationObserver.disconnect(); }; + } else if (config.element instanceof MediaQueryList) { + // Add listening. + config.element.addEventListener( + config.on as keyof MediaQueryListEventMap, + listener, + config.eventOptions || {} + ); + + // Generate the remover. + config.remover = () => { + config.element.removeEventListener( + config.on as keyof MediaQueryListEventMap, + listener, + config.eventOptions || {} + ); + }; } else { // Add listening. config.element.addEventListener(