@solid-primitives/media
Advanced tools
+90
-87
@@ -1,95 +0,98 @@ | ||
| import { type Accessor } from "solid-js"; | ||
| /* @ts-self-types="./index.d.ts" */ | ||
| import { Accessor } from "solid-js"; | ||
| //#region src/index.d.ts | ||
| /** | ||
| * attaches a MediaQuery listener to window, listeneing to changes to provided query | ||
| * @param query Media query to listen for | ||
| * @param callback function called every time the media match changes | ||
| * @returns function removing the listener | ||
| * @example | ||
| * const clear = makeMediaQueryListener("(max-width: 767px)", e => { | ||
| * console.log(e.matches) | ||
| * }); | ||
| * // remove listeners (will happen also on cleanup) | ||
| * clear() | ||
| */ | ||
| export declare function makeMediaQueryListener(query: string | MediaQueryList, callback: (e: MediaQueryListEvent) => void): VoidFunction; | ||
| * attaches a MediaQuery listener to window, listeneing to changes to provided query | ||
| * @param query Media query to listen for | ||
| * @param callback function called every time the media match changes | ||
| * @returns function removing the listener | ||
| * @example | ||
| * const clear = makeMediaQueryListener("(max-width: 767px)", e => { | ||
| * console.log(e.matches) | ||
| * }); | ||
| * // remove listeners (will happen also on cleanup) | ||
| * clear() | ||
| */ | ||
| declare function makeMediaQueryListener(query: string | MediaQueryList, callback: (e: MediaQueryListEvent) => void): VoidFunction; | ||
| /** | ||
| * Creates a very simple and straightforward media query monitor. | ||
| * | ||
| * @param query Media query to listen for | ||
| * @param fallbackState Server fallback state *(Defaults to `false`)* | ||
| * @returns Boolean value if media query is met or not | ||
| * | ||
| * @example | ||
| * ```ts | ||
| * const isSmall = createMediaQuery("(max-width: 767px)"); | ||
| * console.log(isSmall()); | ||
| * ``` | ||
| */ | ||
| export declare function createMediaQuery(query: string, serverFallback?: boolean): () => boolean; | ||
| * Creates a very simple and straightforward media query monitor. | ||
| * | ||
| * @param query Media query to listen for | ||
| * @param fallbackState Server fallback state *(Defaults to `false`)* | ||
| * @returns Boolean value if media query is met or not | ||
| * | ||
| * @example | ||
| * ```ts | ||
| * const isSmall = createMediaQuery("(max-width: 767px)"); | ||
| * console.log(isSmall()); | ||
| * ``` | ||
| */ | ||
| declare function createMediaQuery(query: string, serverFallback?: boolean): Accessor<boolean>; | ||
| /** | ||
| * Provides a signal indicating if the user has requested dark color theme. The setting is being watched with a [Media Query](https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-color-scheme). | ||
| * | ||
| * @param serverFallback value that should be returned on the server — defaults to `false` | ||
| * | ||
| * @returns a boolean signal | ||
| * @example | ||
| * const prefersDark = usePrefersDark(); | ||
| * createEffect(() => { | ||
| * prefersDark() // => boolean | ||
| * }); | ||
| */ | ||
| export declare function createPrefersDark(serverFallback?: boolean): () => boolean; | ||
| * Provides a signal indicating if the user has requested dark color theme. The setting is being watched with a [Media Query](https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-color-scheme). | ||
| * | ||
| * @param serverFallback value that should be returned on the server — defaults to `false` | ||
| * | ||
| * @returns a boolean signal | ||
| * @example | ||
| * const prefersDark = usePrefersDark(); | ||
| * createEffect(() => { | ||
| * prefersDark() // => boolean | ||
| * }); | ||
| */ | ||
| declare function createPrefersDark(serverFallback?: boolean): Accessor<boolean>; | ||
| /** | ||
| * Provides a signal indicating if the user has requested dark color theme. The setting is being watched with a [Media Query](https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-color-scheme). | ||
| * | ||
| * This is a [singleton root primitive](https://github.com/solidjs-community/solid-primitives/tree/main/packages/rootless#createSingletonRoot) except if during hydration. | ||
| * | ||
| * @returns a boolean signal | ||
| * @example | ||
| * const prefersDark = usePrefersDark(); | ||
| * createEffect(() => { | ||
| * prefersDark() // => boolean | ||
| * }); | ||
| */ | ||
| export declare const usePrefersDark: () => Accessor<boolean>; | ||
| export type Breakpoints = Record<string, string>; | ||
| export type Matches<T extends Breakpoints> = { | ||
| readonly [K in keyof T]: K extends "key" ? never : boolean; | ||
| } & { | ||
| key: keyof T; | ||
| * Provides a signal indicating if the user has requested dark color theme. The setting is being watched with a [Media Query](https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-color-scheme). | ||
| * | ||
| * This is a [singleton root primitive](https://github.com/solidjs-community/solid-primitives/tree/main/packages/rootless#createSingletonRoot) except if during hydration. | ||
| * | ||
| * @returns a boolean signal | ||
| * @example | ||
| * const prefersDark = usePrefersDark(); | ||
| * createEffect(() => { | ||
| * prefersDark() // => boolean | ||
| * }); | ||
| */ | ||
| declare const usePrefersDark: () => Accessor<boolean>; | ||
| type Breakpoints = Record<string, string>; | ||
| type Matches<T extends Breakpoints> = { readonly [K in keyof T]: K extends "key" ? never : boolean } & { | ||
| key: keyof T; | ||
| }; | ||
| export interface BreakpointOptions<T extends Breakpoints> { | ||
| /** If true watches changes and reports state reactively */ | ||
| watchChange?: boolean; | ||
| /** Default value of `match` when `window.matchMedia` is not available like during SSR & legacy browsers */ | ||
| fallbackState?: Matches<T>; | ||
| /** Use `min-width` media query for mobile first or `max-width` for desktop first. Defaults to `min-width` */ | ||
| mediaFeature?: string; | ||
| interface BreakpointOptions<T extends Breakpoints> { | ||
| /** If true watches changes and reports state reactively */ | ||
| watchChange?: boolean; | ||
| /** Default value of `match` when `window.matchMedia` is not available like during SSR & legacy browsers */ | ||
| fallbackState?: Matches<T>; | ||
| /** Use `min-width` media query for mobile first or `max-width` for desktop first. Defaults to `min-width` */ | ||
| mediaFeature?: string; | ||
| } | ||
| /** | ||
| * Creates a multi-breakpoint monitor to make responsive components easily. | ||
| * | ||
| * @param breakpoints Map of breakpoint names and their widths | ||
| * @param options Options to customize watch, fallback, responsive mode. | ||
| * @returns map of currently matching breakpoints. | ||
| * | ||
| * @example | ||
| * ```ts | ||
| * const breakpoints = { | ||
| sm: "640px", | ||
| lg: "1024px", | ||
| xl: "1280px", | ||
| }; | ||
| * const matches = createBreakpoints(breakpoints); | ||
| * console.log(matches.lg); | ||
| * ``` | ||
| */ | ||
| export declare function createBreakpoints<T extends Breakpoints>(breakpoints: T, options?: BreakpointOptions<T>): Matches<T>; | ||
| * Creates a multi-breakpoint monitor to make responsive components easily. | ||
| * | ||
| * @param breakpoints Map of breakpoint names and their widths | ||
| * @param options Options to customize watch, fallback, responsive mode. | ||
| * @returns map of currently matching breakpoints. | ||
| * | ||
| * @example | ||
| * ```ts | ||
| * const breakpoints = { | ||
| sm: "640px", | ||
| lg: "1024px", | ||
| xl: "1280px", | ||
| }; | ||
| * const matches = createBreakpoints(breakpoints); | ||
| * console.log(matches.lg); | ||
| * ``` | ||
| */ | ||
| declare function createBreakpoints<T extends Breakpoints>(breakpoints: T, options?: BreakpointOptions<T>): Matches<T>; | ||
| /** | ||
| * Creates a sorted copy of the Breakpoints Object | ||
| * If you want to use the result of `createBreakpoints()` with string coercion: | ||
| * ```ts | ||
| * createBreakpoints(sortBreakpoints({ tablet: "980px", mobile: "640px" })) | ||
| * ``` | ||
| */ | ||
| export declare function sortBreakpoints(breakpoints: Breakpoints): Breakpoints; | ||
| * Creates a sorted copy of the Breakpoints Object | ||
| * If you want to use the result of `createBreakpoints()` with string coercion: | ||
| * ```ts | ||
| * createBreakpoints(sortBreakpoints({ tablet: "980px", mobile: "640px" })) | ||
| * ``` | ||
| */ | ||
| declare function sortBreakpoints(breakpoints: Breakpoints): Breakpoints; | ||
| //#endregion | ||
| export { BreakpointOptions, Breakpoints, Matches, createBreakpoints, createMediaQuery, createPrefersDark, makeMediaQueryListener, sortBreakpoints, usePrefersDark }; |
+116
-117
@@ -1,134 +0,133 @@ | ||
| import {} from "solid-js"; | ||
| /* @ts-self-types="./index.d.ts" */ | ||
| import "solid-js"; | ||
| import { isServer } from "@solidjs/web"; | ||
| import { makeEventListener } from "@solid-primitives/event-listener"; | ||
| import { entries, noop, createHydratableSignal } from "@solid-primitives/utils"; | ||
| import { createHydratableSignal, entries, noop } from "@solid-primitives/utils"; | ||
| import { createHydratableStaticStore } from "@solid-primitives/static-store"; | ||
| import { createHydratableSingletonRoot } from "@solid-primitives/rootless"; | ||
| //#region src/index.ts | ||
| /** | ||
| * attaches a MediaQuery listener to window, listeneing to changes to provided query | ||
| * @param query Media query to listen for | ||
| * @param callback function called every time the media match changes | ||
| * @returns function removing the listener | ||
| * @example | ||
| * const clear = makeMediaQueryListener("(max-width: 767px)", e => { | ||
| * console.log(e.matches) | ||
| * }); | ||
| * // remove listeners (will happen also on cleanup) | ||
| * clear() | ||
| */ | ||
| export function makeMediaQueryListener(query, callback) { | ||
| if (isServer) { | ||
| return noop; | ||
| } | ||
| const mql = typeof query === "string" ? window.matchMedia(query) : query; | ||
| return makeEventListener(mql, "change", callback); | ||
| * attaches a MediaQuery listener to window, listeneing to changes to provided query | ||
| * @param query Media query to listen for | ||
| * @param callback function called every time the media match changes | ||
| * @returns function removing the listener | ||
| * @example | ||
| * const clear = makeMediaQueryListener("(max-width: 767px)", e => { | ||
| * console.log(e.matches) | ||
| * }); | ||
| * // remove listeners (will happen also on cleanup) | ||
| * clear() | ||
| */ | ||
| function makeMediaQueryListener(query, callback) { | ||
| if (isServer) return noop; | ||
| return makeEventListener(typeof query === "string" ? window.matchMedia(query) : query, "change", callback); | ||
| } | ||
| /** | ||
| * Creates a very simple and straightforward media query monitor. | ||
| * | ||
| * @param query Media query to listen for | ||
| * @param fallbackState Server fallback state *(Defaults to `false`)* | ||
| * @returns Boolean value if media query is met or not | ||
| * | ||
| * @example | ||
| * ```ts | ||
| * const isSmall = createMediaQuery("(max-width: 767px)"); | ||
| * console.log(isSmall()); | ||
| * ``` | ||
| */ | ||
| export function createMediaQuery(query, serverFallback = false) { | ||
| if (isServer) { | ||
| return () => serverFallback; | ||
| } | ||
| const mql = window.matchMedia(query); | ||
| const [state, setState] = createHydratableSignal(serverFallback, () => mql.matches); | ||
| const update = () => setState(mql.matches); | ||
| makeEventListener(mql, "change", update); | ||
| return state; | ||
| * Creates a very simple and straightforward media query monitor. | ||
| * | ||
| * @param query Media query to listen for | ||
| * @param fallbackState Server fallback state *(Defaults to `false`)* | ||
| * @returns Boolean value if media query is met or not | ||
| * | ||
| * @example | ||
| * ```ts | ||
| * const isSmall = createMediaQuery("(max-width: 767px)"); | ||
| * console.log(isSmall()); | ||
| * ``` | ||
| */ | ||
| function createMediaQuery(query, serverFallback = false) { | ||
| if (isServer) return () => serverFallback; | ||
| const mql = window.matchMedia(query); | ||
| const [state, setState] = createHydratableSignal(serverFallback, () => mql.matches); | ||
| const update = () => setState(mql.matches); | ||
| makeEventListener(mql, "change", update); | ||
| return state; | ||
| } | ||
| /** | ||
| * Provides a signal indicating if the user has requested dark color theme. The setting is being watched with a [Media Query](https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-color-scheme). | ||
| * | ||
| * @param serverFallback value that should be returned on the server — defaults to `false` | ||
| * | ||
| * @returns a boolean signal | ||
| * @example | ||
| * const prefersDark = usePrefersDark(); | ||
| * createEffect(() => { | ||
| * prefersDark() // => boolean | ||
| * }); | ||
| */ | ||
| export function createPrefersDark(serverFallback) { | ||
| return createMediaQuery("(prefers-color-scheme: dark)", serverFallback); | ||
| * Provides a signal indicating if the user has requested dark color theme. The setting is being watched with a [Media Query](https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-color-scheme). | ||
| * | ||
| * @param serverFallback value that should be returned on the server — defaults to `false` | ||
| * | ||
| * @returns a boolean signal | ||
| * @example | ||
| * const prefersDark = usePrefersDark(); | ||
| * createEffect(() => { | ||
| * prefersDark() // => boolean | ||
| * }); | ||
| */ | ||
| function createPrefersDark(serverFallback) { | ||
| return createMediaQuery("(prefers-color-scheme: dark)", serverFallback); | ||
| } | ||
| /** | ||
| * Provides a signal indicating if the user has requested dark color theme. The setting is being watched with a [Media Query](https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-color-scheme). | ||
| * | ||
| * This is a [singleton root primitive](https://github.com/solidjs-community/solid-primitives/tree/main/packages/rootless#createSingletonRoot) except if during hydration. | ||
| * | ||
| * @returns a boolean signal | ||
| * @example | ||
| * const prefersDark = usePrefersDark(); | ||
| * createEffect(() => { | ||
| * prefersDark() // => boolean | ||
| * }); | ||
| */ | ||
| export const usePrefersDark = /*#__PURE__*/ createHydratableSingletonRoot(createPrefersDark.bind(void 0, false)); | ||
| * Provides a signal indicating if the user has requested dark color theme. The setting is being watched with a [Media Query](https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-color-scheme). | ||
| * | ||
| * This is a [singleton root primitive](https://github.com/solidjs-community/solid-primitives/tree/main/packages/rootless#createSingletonRoot) except if during hydration. | ||
| * | ||
| * @returns a boolean signal | ||
| * @example | ||
| * const prefersDark = usePrefersDark(); | ||
| * createEffect(() => { | ||
| * prefersDark() // => boolean | ||
| * }); | ||
| */ | ||
| const usePrefersDark = /*#__PURE__*/ createHydratableSingletonRoot(createPrefersDark.bind(void 0, false)); | ||
| const getEmptyMatchesFromBreakpoints = (breakpoints) => entries(breakpoints).reduce((matches, [key]) => { | ||
| matches[key] = false; | ||
| return matches; | ||
| matches[key] = false; | ||
| return matches; | ||
| }, {}); | ||
| /** | ||
| * Creates a multi-breakpoint monitor to make responsive components easily. | ||
| * | ||
| * @param breakpoints Map of breakpoint names and their widths | ||
| * @param options Options to customize watch, fallback, responsive mode. | ||
| * @returns map of currently matching breakpoints. | ||
| * | ||
| * @example | ||
| * ```ts | ||
| * const breakpoints = { | ||
| sm: "640px", | ||
| lg: "1024px", | ||
| xl: "1280px", | ||
| }; | ||
| * const matches = createBreakpoints(breakpoints); | ||
| * console.log(matches.lg); | ||
| * ``` | ||
| */ | ||
| export function createBreakpoints(breakpoints, options = {}) { | ||
| const fallback = Object.defineProperty(options.fallbackState ?? getEmptyMatchesFromBreakpoints(breakpoints), "key", { enumerable: false, get: () => Object.keys(breakpoints).pop() }); | ||
| // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition | ||
| if (isServer || !window.matchMedia) | ||
| return fallback; | ||
| const { mediaFeature = "min-width", watchChange = true } = options; | ||
| const [matches, setMatches] = createHydratableStaticStore(fallback, () => { | ||
| const matches = {}; | ||
| entries(breakpoints).forEach(([token, width]) => { | ||
| const mql = window.matchMedia(`(${mediaFeature}: ${width})`); | ||
| matches[token] = mql.matches; | ||
| if (watchChange) | ||
| makeEventListener(mql, "change", (e) => setMatches(token, e.matches)); | ||
| }); | ||
| return matches; | ||
| }); | ||
| return Object.defineProperty(matches, "key", { | ||
| enumerable: false, | ||
| get: () => Object.keys(matches).findLast(token => matches[token]), | ||
| }); | ||
| * Creates a multi-breakpoint monitor to make responsive components easily. | ||
| * | ||
| * @param breakpoints Map of breakpoint names and their widths | ||
| * @param options Options to customize watch, fallback, responsive mode. | ||
| * @returns map of currently matching breakpoints. | ||
| * | ||
| * @example | ||
| * ```ts | ||
| * const breakpoints = { | ||
| sm: "640px", | ||
| lg: "1024px", | ||
| xl: "1280px", | ||
| }; | ||
| * const matches = createBreakpoints(breakpoints); | ||
| * console.log(matches.lg); | ||
| * ``` | ||
| */ | ||
| function createBreakpoints(breakpoints, options = {}) { | ||
| const fallback = Object.defineProperty(options.fallbackState ?? getEmptyMatchesFromBreakpoints(breakpoints), "key", { | ||
| enumerable: false, | ||
| get: () => Object.keys(breakpoints).pop() | ||
| }); | ||
| if (isServer || !window.matchMedia) return fallback; | ||
| const { mediaFeature = "min-width", watchChange = true } = options; | ||
| const [matches, setMatches] = createHydratableStaticStore(fallback, () => { | ||
| const matches = {}; | ||
| entries(breakpoints).forEach(([token, width]) => { | ||
| const mql = window.matchMedia(`(${mediaFeature}: ${width})`); | ||
| matches[token] = mql.matches; | ||
| if (watchChange) makeEventListener(mql, "change", (e) => setMatches(token, e.matches)); | ||
| }); | ||
| return matches; | ||
| }); | ||
| return Object.defineProperty(matches, "key", { | ||
| enumerable: false, | ||
| get: () => Object.keys(matches).findLast((token) => matches[token]) | ||
| }); | ||
| } | ||
| /** | ||
| * Creates a sorted copy of the Breakpoints Object | ||
| * If you want to use the result of `createBreakpoints()` with string coercion: | ||
| * ```ts | ||
| * createBreakpoints(sortBreakpoints({ tablet: "980px", mobile: "640px" })) | ||
| * ``` | ||
| */ | ||
| export function sortBreakpoints(breakpoints) { | ||
| const sorted = entries(breakpoints); | ||
| sorted.sort((x, y) => parseInt(x[1], 10) - parseInt(y[1], 10)); | ||
| return sorted.reduce((obj, [key, value]) => { | ||
| obj[key] = value; | ||
| return obj; | ||
| }, {}); | ||
| * Creates a sorted copy of the Breakpoints Object | ||
| * If you want to use the result of `createBreakpoints()` with string coercion: | ||
| * ```ts | ||
| * createBreakpoints(sortBreakpoints({ tablet: "980px", mobile: "640px" })) | ||
| * ``` | ||
| */ | ||
| function sortBreakpoints(breakpoints) { | ||
| const sorted = entries(breakpoints); | ||
| sorted.sort((x, y) => parseInt(x[1], 10) - parseInt(y[1], 10)); | ||
| return sorted.reduce((obj, [key, value]) => { | ||
| obj[key] = value; | ||
| return obj; | ||
| }, {}); | ||
| } | ||
| //#endregion | ||
| export { createBreakpoints, createMediaQuery, createPrefersDark, makeMediaQueryListener, sortBreakpoints, usePrefersDark }; |
+15
-11
| { | ||
| "name": "@solid-primitives/media", | ||
| "version": "4.0.0-next.0", | ||
| "version": "4.0.0-next.1", | ||
| "description": "Primitives for media query and device features", | ||
@@ -31,3 +31,3 @@ "author": "David Di Biase <dave@solidjs.com>", | ||
| "category": "Display & Media", | ||
| "gzip": 1285 | ||
| "gzip": 1290 | ||
| }, | ||
@@ -59,20 +59,24 @@ "keywords": [ | ||
| }, | ||
| "tsdown": { | ||
| "entry": "src/**/*.{ts,tsx}", | ||
| "outDir": "dist" | ||
| }, | ||
| "dependencies": { | ||
| "@solid-primitives/event-listener": "^3.0.0-next.0", | ||
| "@solid-primitives/rootless": "^2.0.0-next.0", | ||
| "@solid-primitives/static-store": "^1.0.0-next.0", | ||
| "@solid-primitives/utils": "^7.0.0-next.0" | ||
| "@solid-primitives/event-listener": "^3.0.0-next.2", | ||
| "@solid-primitives/rootless": "^2.0.0-next.1", | ||
| "@solid-primitives/static-store": "^1.0.0-next.1", | ||
| "@solid-primitives/utils": "^7.0.0-next.2" | ||
| }, | ||
| "peerDependencies": { | ||
| "@solidjs/web": "^2.0.0-beta.15", | ||
| "solid-js": "^2.0.0-beta.15" | ||
| "@solidjs/web": "^2.0.0-beta.20", | ||
| "solid-js": "^2.0.0-beta.20" | ||
| }, | ||
| "typesVersions": {}, | ||
| "devDependencies": { | ||
| "@solidjs/web": "2.0.0-beta.15", | ||
| "solid-js": "2.0.0-beta.15" | ||
| "@solidjs/web": "2.0.0-beta.20", | ||
| "solid-js": "2.0.0-beta.20" | ||
| }, | ||
| "scripts": { | ||
| "dev": "node --import=@nothing-but/node-resolve-ts --experimental-transform-types ../../scripts/dev.ts", | ||
| "build": "node --import=@nothing-but/node-resolve-ts --experimental-transform-types ../../scripts/build.ts", | ||
| "build": "pnpm -w build", | ||
| "vitest": "vitest -c ../../configs/vitest.config.ts", | ||
@@ -79,0 +83,0 @@ "test": "pnpm run vitest", |
+1
-1
@@ -7,3 +7,3 @@ <p> | ||
| [](https://bundlephobia.com/package/@solid-primitives/media) | ||
| [](https://bundlephobia.com/package/@solid-primitives/media) | ||
| [](https://www.npmjs.com/package/@solid-primitives/media) | ||
@@ -10,0 +10,0 @@ [](https://github.com/solidjs-community/solid-primitives#contribution-process) |
19489
-0.73%