breakpoint-utils
Advanced tools
| import { type BreakpointValue } from './breakpoints'; | ||
| export declare const useUp: (size: BreakpointValue) => boolean; | ||
| export declare const useDown: (size: BreakpointValue) => boolean; | ||
| export declare const useOnly: (size: string) => boolean; | ||
| export declare const useNot: (size: string) => boolean; | ||
| export declare const useBetween: (minSize: BreakpointValue, maxSize: BreakpointValue) => boolean; | ||
| //# sourceMappingURL=useBreakpoint.d.ts.map |
| {"version":3,"file":"useBreakpoint.d.ts","sourceRoot":"","sources":["../src/useBreakpoint.ts"],"names":[],"mappings":"AACA,OAAO,EAML,KAAK,eAAe,EACrB,MAAM,eAAe,CAAC;AA0BvB,eAAO,MAAM,KAAK,GAAI,MAAM,eAAe,KAAG,OAA4C,CAAC;AAC3F,eAAO,MAAM,OAAO,GAAI,MAAM,eAAe,KAAG,OAA8C,CAAC;AAC/F,eAAO,MAAM,OAAO,GAAI,MAAM,MAAM,KAAG,OAA8C,CAAC;AACtF,eAAO,MAAM,MAAM,GAAI,MAAM,MAAM,KAAG,OAA6C,CAAC;AACpF,eAAO,MAAM,UAAU,GAAI,SAAS,eAAe,EAAE,SAAS,eAAe,KAAG,OAA6D,CAAC"} |
| import { useCallback, useMemo, useSyncExternalStore } from 'react'; | ||
| import { buildUpQuery, buildDownQuery, buildOnlyQuery, buildNotQuery, buildBetweenQuery, } from './breakpoints'; | ||
| const useMediaQuery = (query) => { | ||
| const mql = useMemo(() => typeof window !== 'undefined' ? window.matchMedia(query) : null, [query]); | ||
| const subscribe = useCallback((callback) => { | ||
| if (!mql) | ||
| return () => { }; | ||
| mql.addEventListener('change', callback); | ||
| return () => mql.removeEventListener('change', callback); | ||
| }, [mql]); | ||
| const getSnapshot = useCallback(() => { | ||
| var _a; | ||
| return (_a = mql === null || mql === void 0 ? void 0 : mql.matches) !== null && _a !== void 0 ? _a : false; | ||
| }, [mql]); | ||
| const getServerSnapshot = useCallback(() => false, []); | ||
| return useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot); | ||
| }; | ||
| export const useUp = (size) => useMediaQuery(buildUpQuery(size)); | ||
| export const useDown = (size) => useMediaQuery(buildDownQuery(size)); | ||
| export const useOnly = (size) => useMediaQuery(buildOnlyQuery(size)); | ||
| export const useNot = (size) => useMediaQuery(buildNotQuery(size)); | ||
| export const useBetween = (minSize, maxSize) => useMediaQuery(buildBetweenQuery(minSize, maxSize)); | ||
| //# sourceMappingURL=useBreakpoint.js.map |
| {"version":3,"file":"useBreakpoint.js","sourceRoot":"","sources":["../src/useBreakpoint.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,OAAO,EAAE,oBAAoB,EAAE,MAAM,OAAO,CAAC;AACnE,OAAO,EACL,YAAY,EACZ,cAAc,EACd,cAAc,EACd,aAAa,EACb,iBAAiB,GAElB,MAAM,eAAe,CAAC;AAEvB,MAAM,aAAa,GAAG,CAAC,KAAa,EAAW,EAAE;IAC/C,MAAM,GAAG,GAAG,OAAO,CACjB,GAAG,EAAE,CAAC,OAAO,MAAM,KAAK,WAAW,CAAC,CAAC,CAAC,MAAM,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,EACrE,CAAC,KAAK,CAAC,CACR,CAAC;IAEF,MAAM,SAAS,GAAG,WAAW,CAC3B,CAAC,QAAoB,EAAE,EAAE;QACvB,IAAI,CAAC,GAAG;YAAE,OAAO,GAAG,EAAE,GAAE,CAAC,CAAC;QAC1B,GAAG,CAAC,gBAAgB,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;QACzC,OAAO,GAAG,EAAE,CAAC,GAAG,CAAC,mBAAmB,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;IAC3D,CAAC,EACD,CAAC,GAAG,CAAC,CACN,CAAC;IAEF,MAAM,WAAW,GAAG,WAAW,CAAC,GAAG,EAAE;;QACnC,OAAO,MAAA,GAAG,aAAH,GAAG,uBAAH,GAAG,CAAE,OAAO,mCAAI,KAAK,CAAC;IAC/B,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;IAEV,MAAM,iBAAiB,GAAG,WAAW,CAAC,GAAG,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;IAEvD,OAAO,oBAAoB,CAAC,SAAS,EAAE,WAAW,EAAE,iBAAiB,CAAC,CAAC;AACzE,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,KAAK,GAAG,CAAC,IAAqB,EAAW,EAAE,CAAC,aAAa,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC,CAAC;AAC3F,MAAM,CAAC,MAAM,OAAO,GAAG,CAAC,IAAqB,EAAW,EAAE,CAAC,aAAa,CAAC,cAAc,CAAC,IAAI,CAAC,CAAC,CAAC;AAC/F,MAAM,CAAC,MAAM,OAAO,GAAG,CAAC,IAAY,EAAW,EAAE,CAAC,aAAa,CAAC,cAAc,CAAC,IAAI,CAAC,CAAC,CAAC;AACtF,MAAM,CAAC,MAAM,MAAM,GAAG,CAAC,IAAY,EAAW,EAAE,CAAC,aAAa,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC,CAAC;AACpF,MAAM,CAAC,MAAM,UAAU,GAAG,CAAC,OAAwB,EAAE,OAAwB,EAAW,EAAE,CAAC,aAAa,CAAC,iBAAiB,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC"} |
| export type Breakpoints = Record<string, number>; | ||
| export type BreakpointValue = string | number; | ||
| /** | ||
| * Set custom breakpoints. Call this at app startup before any components mount. | ||
| * Calling after mount will not update active hook subscriptions until the next re-render. | ||
| */ | ||
| export declare const configure: <T extends Breakpoints>(customBreakpoints: T) => void; | ||
| export declare const getBreakpoints: () => Breakpoints; | ||
| export declare const up: (size: string) => boolean; | ||
| export declare const down: (size: string) => boolean; | ||
| /** | ||
| * @internal Query builders - used by hook layer, not part of public API | ||
| */ | ||
| declare const buildUpQuery: (size: BreakpointValue) => string; | ||
| declare const buildDownQuery: (size: BreakpointValue) => string; | ||
| declare const buildOnlyQuery: (size: string) => string; | ||
| declare const buildNotQuery: (size: string) => string; | ||
| declare const buildBetweenQuery: (minSize: BreakpointValue, maxSize: BreakpointValue) => string; | ||
| export declare const up: (size: BreakpointValue) => boolean; | ||
| export declare const down: (size: BreakpointValue) => boolean; | ||
| export declare const only: (size: string) => boolean; | ||
| export declare const not: (size: string) => boolean; | ||
| export declare const between: (minSize: string, maxSize: string) => boolean; | ||
| export declare const between: (minSize: BreakpointValue, maxSize: BreakpointValue) => boolean; | ||
| export { buildUpQuery, buildDownQuery, buildOnlyQuery, buildNotQuery, buildBetweenQuery }; | ||
| //# sourceMappingURL=breakpoints.d.ts.map |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"breakpoints.d.ts","sourceRoot":"","sources":["../src/breakpoints.ts"],"names":[],"mappings":"AAWA,MAAM,MAAM,WAAW,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;AAWjD,eAAO,MAAM,SAAS,GAAI,CAAC,SAAS,WAAW,EAAE,mBAAmB,CAAC,SAGpE,CAAC;AAEF,eAAO,MAAM,cAAc,mBAA2B,CAAC;AAcvD,eAAO,MAAM,EAAE,GAAI,MAAM,MAAM,YAG9B,CAAC;AAEF,eAAO,MAAM,IAAI,GAAI,MAAM,MAAM,YAGhC,CAAC;AAEF,eAAO,MAAM,IAAI,GAAI,MAAM,MAAM,YAMhC,CAAC;AAEF,eAAO,MAAM,GAAG,GAAI,MAAM,MAAM,YAM/B,CAAC;AAEF,eAAO,MAAM,OAAO,GAAI,SAAS,MAAM,EAAE,SAAS,MAAM,YAOvD,CAAC"} | ||
| {"version":3,"file":"breakpoints.d.ts","sourceRoot":"","sources":["../src/breakpoints.ts"],"names":[],"mappings":"AASA,MAAM,MAAM,WAAW,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;AACjD,MAAM,MAAM,eAAe,GAAG,MAAM,GAAG,MAAM,CAAC;AAW9C;;;GAGG;AACH,eAAO,MAAM,SAAS,GAAI,CAAC,SAAS,WAAW,EAAE,mBAAmB,CAAC,SAGpE,CAAC;AAEF,eAAO,MAAM,cAAc,mBAA2B,CAAC;AAoBvD;;GAEG;AACH,QAAA,MAAM,YAAY,GAAI,MAAM,eAAe,KAAG,MAE7C,CAAC;AAEF,QAAA,MAAM,cAAc,GAAI,MAAM,eAAe,KAAG,MAE/C,CAAC;AAEF,QAAA,MAAM,cAAc,GAAI,MAAM,MAAM,KAAG,MAOtC,CAAC;AAEF,QAAA,MAAM,aAAa,GAAI,MAAM,MAAM,KAAG,MAOrC,CAAC;AAEF,QAAA,MAAM,iBAAiB,GAAI,SAAS,eAAe,EAAE,SAAS,eAAe,KAAG,MAO/E,CAAC;AAQF,eAAO,MAAM,EAAE,GAAI,MAAM,eAAe,KAAG,OAAyC,CAAC;AACrF,eAAO,MAAM,IAAI,GAAI,MAAM,eAAe,KAAG,OAA2C,CAAC;AACzF,eAAO,MAAM,IAAI,GAAI,MAAM,MAAM,KAAG,OAA2C,CAAC;AAChF,eAAO,MAAM,GAAG,GAAI,MAAM,MAAM,KAAG,OAA0C,CAAC;AAC9E,eAAO,MAAM,OAAO,GAAI,SAAS,eAAe,EAAE,SAAS,eAAe,KAAG,OAA0D,CAAC;AAGxI,OAAO,EAAE,YAAY,EAAE,cAAc,EAAE,cAAc,EAAE,aAAa,EAAE,iBAAiB,EAAE,CAAC"} |
+45
-20
@@ -1,2 +0,1 @@ | ||
| import viewport from './viewport'; | ||
| const defaultBreakpoints = { | ||
@@ -16,2 +15,6 @@ xs: 480, | ||
| updateSortedBreakpoints(); | ||
| /** | ||
| * Set custom breakpoints. Call this at app startup before any components mount. | ||
| * Calling after mount will not update active hook subscriptions until the next re-render. | ||
| */ | ||
| export const configure = (customBreakpoints) => { | ||
@@ -27,2 +30,8 @@ currentBreakpoints = customBreakpoints; | ||
| }; | ||
| const resolveSize = (size) => { | ||
| if (typeof size === 'number') | ||
| return size; | ||
| validateBreakpoint(size); | ||
| return currentBreakpoints[size]; | ||
| }; | ||
| const getNextBreakpoint = (size) => { | ||
@@ -33,32 +42,48 @@ const currentIndex = sortedBreakpoints.findIndex(([name]) => name === size); | ||
| }; | ||
| export const up = (size) => { | ||
| validateBreakpoint(size); | ||
| return viewport.getWidth() >= currentBreakpoints[size]; | ||
| /** | ||
| * @internal Query builders - used by hook layer, not part of public API | ||
| */ | ||
| const buildUpQuery = (size) => { | ||
| return `(min-width: ${resolveSize(size)}px)`; | ||
| }; | ||
| export const down = (size) => { | ||
| validateBreakpoint(size); | ||
| return viewport.getWidth() < currentBreakpoints[size]; | ||
| const buildDownQuery = (size) => { | ||
| return `(max-width: ${resolveSize(size) - 0.02}px)`; | ||
| }; | ||
| export const only = (size) => { | ||
| const buildOnlyQuery = (size) => { | ||
| validateBreakpoint(size); | ||
| const width = viewport.getWidth(); | ||
| const min = currentBreakpoints[size]; | ||
| const max = getNextBreakpoint(size); | ||
| return width >= min && width < max; | ||
| return max === Infinity | ||
| ? `(min-width: ${min}px)` | ||
| : `(min-width: ${min}px) and (max-width: ${max - 0.02}px)`; | ||
| }; | ||
| export const not = (size) => { | ||
| const buildNotQuery = (size) => { | ||
| validateBreakpoint(size); | ||
| const width = viewport.getWidth(); | ||
| const min = currentBreakpoints[size]; | ||
| const max = getNextBreakpoint(size); | ||
| return width < min || width >= max; | ||
| return max === Infinity | ||
| ? `(max-width: ${min - 0.02}px)` | ||
| : `(max-width: ${min - 0.02}px), (min-width: ${max}px)`; | ||
| }; | ||
| export const between = (minSize, maxSize) => { | ||
| validateBreakpoint(minSize); | ||
| validateBreakpoint(maxSize); | ||
| const width = viewport.getWidth(); | ||
| const min = currentBreakpoints[minSize]; | ||
| const max = currentBreakpoints[maxSize]; | ||
| return width >= min && width < max; | ||
| const buildBetweenQuery = (minSize, maxSize) => { | ||
| const min = resolveSize(minSize); | ||
| const max = resolveSize(maxSize); | ||
| if (min >= max) { | ||
| throw new Error(`minSize "${minSize}" (${min}px) must be smaller than maxSize "${maxSize}" (${max}px)`); | ||
| } | ||
| return `(min-width: ${min}px) and (max-width: ${max - 0.02}px)`; | ||
| }; | ||
| // Imperative API (use in handlers, utils, non-React code) | ||
| const matchMedia = (query) => { | ||
| if (typeof window === 'undefined') | ||
| return false; | ||
| return window.matchMedia(query).matches; | ||
| }; | ||
| export const up = (size) => matchMedia(buildUpQuery(size)); | ||
| export const down = (size) => matchMedia(buildDownQuery(size)); | ||
| export const only = (size) => matchMedia(buildOnlyQuery(size)); | ||
| export const not = (size) => matchMedia(buildNotQuery(size)); | ||
| export const between = (minSize, maxSize) => matchMedia(buildBetweenQuery(minSize, maxSize)); | ||
| // Export query builders for hook layer | ||
| export { buildUpQuery, buildDownQuery, buildOnlyQuery, buildNotQuery, buildBetweenQuery }; | ||
| //# sourceMappingURL=breakpoints.js.map |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"breakpoints.js","sourceRoot":"","sources":["../src/breakpoints.ts"],"names":[],"mappings":"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"} | ||
| {"version":3,"file":"breakpoints.js","sourceRoot":"","sources":["../src/breakpoints.ts"],"names":[],"mappings":"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"} |
+2
-1
| export { configure, getBreakpoints, up, down, only, not, between } from './breakpoints'; | ||
| export type { Breakpoints } from './breakpoints'; | ||
| export type { Breakpoints, BreakpointValue } from './breakpoints'; | ||
| export { useUp, useDown, useOnly, useNot, useBetween } from './useBreakpoint'; | ||
| export { default as useViewport } from './useViewport'; | ||
| export { default as viewport } from './viewport'; | ||
| //# sourceMappingURL=index.d.ts.map |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,cAAc,EAAE,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,GAAG,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AACxF,YAAY,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AACjD,OAAO,EAAE,OAAO,IAAI,WAAW,EAAE,MAAM,eAAe,CAAC;AACvD,OAAO,EAAE,OAAO,IAAI,QAAQ,EAAE,MAAM,YAAY,CAAC"} | ||
| {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,cAAc,EAAE,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,GAAG,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AACxF,YAAY,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAClE,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC9E,OAAO,EAAE,OAAO,IAAI,WAAW,EAAE,MAAM,eAAe,CAAC;AACvD,OAAO,EAAE,OAAO,IAAI,QAAQ,EAAE,MAAM,YAAY,CAAC"} |
+1
-0
| export { configure, getBreakpoints, up, down, only, not, between } from './breakpoints'; | ||
| export { useUp, useDown, useOnly, useNot, useBetween } from './useBreakpoint'; | ||
| export { default as useViewport } from './useViewport'; | ||
| export { default as viewport } from './viewport'; | ||
| //# sourceMappingURL=index.js.map |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,cAAc,EAAE,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,GAAG,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AAExF,OAAO,EAAE,OAAO,IAAI,WAAW,EAAE,MAAM,eAAe,CAAC;AACvD,OAAO,EAAE,OAAO,IAAI,QAAQ,EAAE,MAAM,YAAY,CAAC"} | ||
| {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,cAAc,EAAE,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,GAAG,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AAExF,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC9E,OAAO,EAAE,OAAO,IAAI,WAAW,EAAE,MAAM,eAAe,CAAC;AACvD,OAAO,EAAE,OAAO,IAAI,QAAQ,EAAE,MAAM,YAAY,CAAC"} |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"useViewport.d.ts","sourceRoot":"","sources":["../src/useViewport.ts"],"names":[],"mappings":"AAGA,QAAA,MAAM,WAAW,cAahB,CAAC;AAEF,eAAe,WAAW,CAAC"} | ||
| {"version":3,"file":"useViewport.d.ts","sourceRoot":"","sources":["../src/useViewport.ts"],"names":[],"mappings":"AAOA,QAAA,MAAM,WAAW,cAEhB,CAAC;AAEF,eAAe,WAAW,CAAC"} |
+5
-10
@@ -1,15 +0,10 @@ | ||
| import { useEffect, useState } from 'react'; | ||
| import { useSyncExternalStore } from 'react'; | ||
| import viewport from './viewport'; | ||
| const subscribe = (callback) => viewport.onResize(callback); | ||
| const getSnapshot = () => viewport.getWidth(); | ||
| const getServerSnapshot = () => 0; | ||
| const useViewport = () => { | ||
| const [width, setWidth] = useState(viewport.getWidth()); | ||
| useEffect(() => { | ||
| const handler = () => setWidth(viewport.getWidth()); | ||
| const removeHandler = viewport.onResize(handler); | ||
| return () => { | ||
| removeHandler(); | ||
| }; | ||
| }, []); | ||
| return width; | ||
| return useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot); | ||
| }; | ||
| export default useViewport; | ||
| //# sourceMappingURL=useViewport.js.map |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"useViewport.js","sourceRoot":"","sources":["../src/useViewport.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AAC5C,OAAO,QAAQ,MAAM,YAAY,CAAC;AAElC,MAAM,WAAW,GAAG,GAAG,EAAE;IACvB,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,GAAG,QAAQ,CAAC,QAAQ,CAAC,QAAQ,EAAE,CAAC,CAAC;IAExD,SAAS,CAAC,GAAG,EAAE;QACb,MAAM,OAAO,GAAG,GAAG,EAAE,CAAC,QAAQ,CAAC,QAAQ,CAAC,QAAQ,EAAE,CAAC,CAAC;QACpD,MAAM,aAAa,GAAG,QAAQ,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;QAEjD,OAAO,GAAG,EAAE;YACV,aAAa,EAAE,CAAC;QAClB,CAAC,CAAC;IACJ,CAAC,EAAE,EAAE,CAAC,CAAC;IAEP,OAAO,KAAK,CAAC;AACf,CAAC,CAAC;AAEF,eAAe,WAAW,CAAC"} | ||
| {"version":3,"file":"useViewport.js","sourceRoot":"","sources":["../src/useViewport.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAE,MAAM,OAAO,CAAC;AAC7C,OAAO,QAAQ,MAAM,YAAY,CAAC;AAElC,MAAM,SAAS,GAAG,CAAC,QAAoB,EAAE,EAAE,CAAC,QAAQ,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;AACxE,MAAM,WAAW,GAAG,GAAG,EAAE,CAAC,QAAQ,CAAC,QAAQ,EAAE,CAAC;AAC9C,MAAM,iBAAiB,GAAG,GAAG,EAAE,CAAC,CAAC,CAAC;AAElC,MAAM,WAAW,GAAG,GAAG,EAAE;IACvB,OAAO,oBAAoB,CAAC,SAAS,EAAE,WAAW,EAAE,iBAAiB,CAAC,CAAC;AACzE,CAAC,CAAC;AAEF,eAAe,WAAW,CAAC"} |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"viewport.d.ts","sourceRoot":"","sources":["../src/viewport.ts"],"names":[],"mappings":"AAAA,cAAM,cAAc;IAClB,OAAO,CAAC,KAAK,CAAS;IACtB,OAAO,CAAC,aAAa,CAAiB;;IAUtC,OAAO,CAAC,YAAY,CAGlB;IAEF,QAAQ;IAIR,QAAQ,CAAC,OAAO,EAAE,MAAM,IAAI;CAM7B;AAED,QAAA,MAAM,cAAc,gBAAuB,CAAC;AAC5C,eAAe,cAAc,CAAC"} | ||
| {"version":3,"file":"viewport.d.ts","sourceRoot":"","sources":["../src/viewport.ts"],"names":[],"mappings":"AAAA,cAAM,cAAc;IAClB,OAAO,CAAC,KAAK,CAAS;IACtB,OAAO,CAAC,aAAa,CAAkB;;IAUvC,OAAO,CAAC,YAAY,CAGlB;IAEF,QAAQ;IAIR,QAAQ,CAAC,OAAO,EAAE,MAAM,IAAI;CAM7B;AAED,QAAA,MAAM,cAAc,gBAAuB,CAAC;AAC5C,eAAe,cAAc,CAAC"} |
+3
-3
@@ -8,3 +8,3 @@ class ViewportHelper { | ||
| this.width = typeof window !== 'undefined' ? window.innerWidth : 0; | ||
| this.eventHandlers = []; | ||
| this.eventHandlers = new Set(); | ||
| if (typeof window !== 'undefined') { | ||
@@ -18,5 +18,5 @@ window.addEventListener('resize', this.handleResize); | ||
| onResize(handler) { | ||
| this.eventHandlers.push(handler); | ||
| this.eventHandlers.add(handler); | ||
| return () => { | ||
| this.eventHandlers = this.eventHandlers.filter(h => h !== handler); | ||
| this.eventHandlers.delete(handler); | ||
| }; | ||
@@ -23,0 +23,0 @@ } |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"viewport.js","sourceRoot":"","sources":["../src/viewport.ts"],"names":[],"mappings":"AAAA,MAAM,cAAc;IAIlB;QAQQ,iBAAY,GAAG,GAAG,EAAE;YAC1B,IAAI,CAAC,KAAK,GAAG,MAAM,CAAC,UAAU,CAAC;YAC/B,IAAI,CAAC,aAAa,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,CAAC;QACnD,CAAC,CAAC;QAVA,IAAI,CAAC,KAAK,GAAG,OAAO,MAAM,KAAK,WAAW,CAAC,CAAC,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC;QACnE,IAAI,CAAC,aAAa,GAAG,EAAE,CAAC;QACxB,IAAI,OAAO,MAAM,KAAK,WAAW,EAAE,CAAC;YAClC,MAAM,CAAC,gBAAgB,CAAC,QAAQ,EAAE,IAAI,CAAC,YAAY,CAAC,CAAC;QACvD,CAAC;IACH,CAAC;IAOD,QAAQ;QACN,OAAO,IAAI,CAAC,KAAK,CAAC;IACpB,CAAC;IAED,QAAQ,CAAC,OAAmB;QAC1B,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QACjC,OAAO,GAAG,EAAE;YACV,IAAI,CAAC,aAAa,GAAG,IAAI,CAAC,aAAa,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,KAAK,OAAO,CAAC,CAAC;QACrE,CAAC,CAAC;IACJ,CAAC;CACF;AAED,MAAM,cAAc,GAAG,IAAI,cAAc,EAAE,CAAC;AAC5C,eAAe,cAAc,CAAC"} | ||
| {"version":3,"file":"viewport.js","sourceRoot":"","sources":["../src/viewport.ts"],"names":[],"mappings":"AAAA,MAAM,cAAc;IAIlB;QAQQ,iBAAY,GAAG,GAAG,EAAE;YAC1B,IAAI,CAAC,KAAK,GAAG,MAAM,CAAC,UAAU,CAAC;YAC/B,IAAI,CAAC,aAAa,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,CAAC;QACnD,CAAC,CAAC;QAVA,IAAI,CAAC,KAAK,GAAG,OAAO,MAAM,KAAK,WAAW,CAAC,CAAC,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC;QACnE,IAAI,CAAC,aAAa,GAAG,IAAI,GAAG,EAAE,CAAC;QAC/B,IAAI,OAAO,MAAM,KAAK,WAAW,EAAE,CAAC;YAClC,MAAM,CAAC,gBAAgB,CAAC,QAAQ,EAAE,IAAI,CAAC,YAAY,CAAC,CAAC;QACvD,CAAC;IACH,CAAC;IAOD,QAAQ;QACN,OAAO,IAAI,CAAC,KAAK,CAAC;IACpB,CAAC;IAED,QAAQ,CAAC,OAAmB;QAC1B,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;QAChC,OAAO,GAAG,EAAE;YACV,IAAI,CAAC,aAAa,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;QACrC,CAAC,CAAC;IACJ,CAAC;CACF;AAED,MAAM,cAAc,GAAG,IAAI,cAAc,EAAE,CAAC;AAC5C,eAAe,cAAc,CAAC"} |
+12
-3
| { | ||
| "name": "breakpoint-utils", | ||
| "version": "1.0.0", | ||
| "description": "Viewport width utility with breakpoint functions for React", | ||
| "version": "2.0.0", | ||
| "description": "Viewport width utility with optional breakpoint functions for React", | ||
| "main": "dist/index.js", | ||
| "types": "dist/index.d.ts", | ||
| "files": [ | ||
| "dist", | ||
| "README.md" | ||
| ], | ||
| "scripts": { | ||
@@ -12,4 +16,9 @@ "build": "tsc", | ||
| "peerDependencies": { | ||
| "react": ">=16.8.0" | ||
| "react": ">=18.0.0" | ||
| }, | ||
| "peerDependenciesMeta": { | ||
| "react": { | ||
| "optional": true | ||
| } | ||
| }, | ||
| "devDependencies": { | ||
@@ -16,0 +25,0 @@ "@types/react": "^18.2.0", |
+119
-70
@@ -14,35 +14,29 @@ # breakpoint-utils | ||
| - **Configurable breakpoints** - Use defaults or define your own | ||
| - **Responsive utilities** - `up`, `down`, `only`, `not`, `between` | ||
| - **React hook** - `useViewport` for reactive viewport width | ||
| - **Imperative API** - `up`, `down`, `only`, `not`, `between` for use anywhere | ||
| - **React hooks** - `useUp`, `useDown`, `useOnly`, `useNot`, `useBetween` for reactive components | ||
| - **Viewport measurements** - `useViewport` for pixel-based calculations | ||
| - **CSS parity** - Uses `matchMedia` for exact CSS behavior | ||
| - **Lightweight** - Minimal dependencies | ||
| - **TypeScript** - Full type safety | ||
| ## Quick Start | ||
| ## Default Breakpoints | ||
| ### Default Breakpoints | ||
| | Name | Min Width | | ||
| |------|-----------| | ||
| | `xs` | 480px | | ||
| | `sm` | 640px | | ||
| | `md` | 768px | | ||
| | `lg` | 1024px | | ||
| | `xl` | 1280px | | ||
| | `2xl` | 1536px | | ||
| ```typescript | ||
| import { up, down, only } from 'breakpoint-utils'; | ||
| ## Configuration | ||
| // Default breakpoints: xs: 480, sm: 640, md: 768, lg: 1024, xl: 1280, 2xl: 1536 | ||
| ### `configure(breakpoints)` | ||
| if (up('md')) { | ||
| // viewport >= 768px | ||
| } | ||
| Replace the default breakpoints with your own. Must be called before any components mount. | ||
| if (down('lg')) { | ||
| // viewport < 1024px | ||
| } | ||
| if (only('sm')) { | ||
| // 640px <= viewport < 768px | ||
| } | ||
| ``` | ||
| ### Custom Breakpoints | ||
| ```typescript | ||
| import { configure, up, down } from 'breakpoint-utils'; | ||
| import { configure } from 'breakpoint-utils'; | ||
| // Define your own breakpoints | ||
| configure({ | ||
@@ -54,22 +48,4 @@ mobile: 320, | ||
| }); | ||
| if (up('tablet')) { | ||
| // viewport >= 768px | ||
| } | ||
| ``` | ||
| ## API | ||
| ### `configure(breakpoints)` | ||
| Set custom breakpoints. Must be called before using utility functions. | ||
| ```typescript | ||
| configure({ | ||
| small: 600, | ||
| medium: 900, | ||
| large: 1200, | ||
| }); | ||
| ``` | ||
| ### `getBreakpoints()` | ||
@@ -84,2 +60,8 @@ | ||
| ## Imperative API | ||
| Use these in event handlers, utility functions, or anywhere outside of React rendering. | ||
| All functions accept a named breakpoint string. `up`, `down`, and `between` also accept a number for ad-hoc pixel values. | ||
| ### `up(size)` | ||
@@ -90,3 +72,4 @@ | ||
| ```typescript | ||
| up('md') // viewport >= 768px | ||
| up('md') // viewport >= 768px | ||
| up(900) // viewport >= 900px | ||
| ``` | ||
@@ -96,6 +79,7 @@ | ||
| Returns `true` if viewport width is < the breakpoint. | ||
| Returns `true` if viewport is **below** the breakpoint (does not include it). | ||
| ```typescript | ||
| down('lg') // viewport < 1024px | ||
| down(900) // viewport < 900px | ||
| ``` | ||
@@ -124,46 +108,111 @@ | ||
| ```typescript | ||
| between('sm', 'lg') // 640px <= viewport < 1024px | ||
| between('sm', 'lg') // 640px <= viewport < 1024px | ||
| between(600, 900) // 600px <= viewport < 900px | ||
| between('sm', 900) // mix named + numeric | ||
| ``` | ||
| ## React Hook | ||
| > **Note:** These functions return a one-shot result at call time. In React, use the hooks below for reactive updates. Outside React, you can pair with the `viewport` singleton to re-check on viewport changes: | ||
| > | ||
| > ```typescript | ||
| > import { up, viewport } from 'breakpoint-utils'; | ||
| > | ||
| > // viewport exposes getWidth() and onResize() for manual subscription | ||
| > const unsubscribe = viewport.onResize(() => { | ||
| > console.log('Is desktop:', up('lg')); | ||
| > }); | ||
| > | ||
| > // Clean up when no longer needed | ||
| > unsubscribe(); | ||
| > ``` | ||
| ## React Hooks | ||
| Each hook mirrors its imperative counterpart but reactively re-renders the component when the result changes. Like the imperative API, `useUp`, `useDown`, and `useBetween` accept numbers as well as named breakpoints. | ||
| ### `useUp(size)` | ||
| Returns `true` if viewport width is >= the breakpoint. | ||
| ```typescript | ||
| const isDesktop = useUp('lg'); // viewport >= 1024px | ||
| const isWide = useUp(900); // viewport >= 900px | ||
| ``` | ||
| ### `useDown(size)` | ||
| Returns `true` if viewport is **below** the breakpoint (does not include it). | ||
| ```typescript | ||
| const isMobile = useDown('md'); // viewport < 768px | ||
| const isNarrow = useDown(600); // viewport < 600px | ||
| ``` | ||
| ### `useOnly(size)` | ||
| Returns `true` if viewport is within the breakpoint range. | ||
| ```typescript | ||
| const isTabletOnly = useOnly('md'); // 768px <= viewport < 1024px | ||
| ``` | ||
| ### `useNot(size)` | ||
| Returns `true` if viewport is outside the breakpoint range. | ||
| ```typescript | ||
| const isNotTablet = useNot('md'); // viewport < 768px OR viewport >= 1024px | ||
| ``` | ||
| ### `useBetween(minSize, maxSize)` | ||
| Returns `true` if viewport is between two breakpoints. | ||
| ```typescript | ||
| const isSmallToMedium = useBetween('sm', 'lg'); // 640px <= viewport < 1024px | ||
| const isCustomRange = useBetween(600, 900); // 600px <= viewport < 900px | ||
| ``` | ||
| > **SSR Note:** All hooks return `false` during server-side rendering until hydration completes. | ||
| ### `useViewport()` | ||
| React hook that returns the current viewport width and updates on resize. | ||
| Returns the current viewport width in pixels. Use for pixel-based calculations (drag/resize logic, canvas, charts, virtualization): | ||
| ```typescript | ||
| import { useViewport, configure, up } from 'breakpoint-utils'; | ||
| import { useViewport } from 'breakpoint-utils'; | ||
| // Optional: configure custom breakpoints | ||
| configure({ | ||
| mobile: 375, | ||
| tablet: 768, | ||
| desktop: 1024, | ||
| }); | ||
| function ResizablePanel() { | ||
| const width = useViewport(); | ||
| const panelWidth = Math.min(width * 0.8, 600); | ||
| function MyComponent() { | ||
| const width = useViewport(); | ||
| return ( | ||
| <div> | ||
| <p>Viewport width: {width}px</p> | ||
| {up('tablet') && <p>Tablet or larger</p>} | ||
| </div> | ||
| ); | ||
| return <div style={{ width: panelWidth }} />; | ||
| } | ||
| ``` | ||
| ## Direct Viewport Access | ||
| > **Note:** Use breakpoint hooks (`useUp`, etc.) for responsive layouts. Use `useViewport` only when you need actual pixel values for calculations. | ||
| ## Migrating from v1.0.0 | ||
| In v1.0.0, reactive breakpoint checks required `useViewport()` to trigger re-renders: | ||
| ```typescript | ||
| import { viewport } from 'breakpoint-utils'; | ||
| import { useViewport, up } from 'breakpoint-utils'; | ||
| const width = viewport.getWidth(); | ||
| function MyComponent() { | ||
| useViewport(); // re-renders on every pixel change | ||
| const isDesktop = up('lg'); | ||
| return isDesktop ? <DesktopNav /> : <MobileNav />; | ||
| } | ||
| ``` | ||
| const unsubscribe = viewport.onResize(() => { | ||
| console.log('Viewport resized:', viewport.getWidth()); | ||
| }); | ||
| This still works, but the dedicated hooks are preferred — they only re-render when a breakpoint boundary is crossed: | ||
| // Clean up | ||
| unsubscribe(); | ||
| ```typescript | ||
| // v2.0.0+ — dedicated hooks | ||
| import { useUp } from 'breakpoint-utils'; | ||
| function MyComponent() { | ||
| const isDesktop = useUp('lg'); // re-renders only at 1024px boundary | ||
| return isDesktop ? <DesktopNav /> : <MobileNav />; | ||
| } | ||
| ``` | ||
@@ -170,0 +219,0 @@ |
| import viewport from './viewport'; | ||
| const defaultBreakpoints = { | ||
| xs: 480, | ||
| sm: 640, | ||
| md: 768, | ||
| lg: 1024, | ||
| xl: 1280, | ||
| '2xl': 1536, | ||
| } as const; | ||
| export type Breakpoints = Record<string, number>; | ||
| let currentBreakpoints: Breakpoints = { ...defaultBreakpoints }; | ||
| let sortedBreakpoints: [string, number][] = []; | ||
| const updateSortedBreakpoints = () => { | ||
| sortedBreakpoints = Object.entries(currentBreakpoints).sort((a, b) => a[1] - b[1]); | ||
| }; | ||
| updateSortedBreakpoints(); | ||
| export const configure = <T extends Breakpoints>(customBreakpoints: T) => { | ||
| currentBreakpoints = customBreakpoints; | ||
| updateSortedBreakpoints(); | ||
| }; | ||
| export const getBreakpoints = () => currentBreakpoints; | ||
| const validateBreakpoint = (size: string): void => { | ||
| if (!(size in currentBreakpoints)) { | ||
| throw new Error(`Invalid breakpoint: "${size}". Available breakpoints: ${Object.keys(currentBreakpoints).join(', ')}`); | ||
| } | ||
| }; | ||
| const getNextBreakpoint = (size: string): number => { | ||
| const currentIndex = sortedBreakpoints.findIndex(([name]) => name === size); | ||
| const next = sortedBreakpoints[currentIndex + 1]; | ||
| return next ? next[1] : Infinity; | ||
| }; | ||
| export const up = (size: string) => { | ||
| validateBreakpoint(size); | ||
| return viewport.getWidth() >= currentBreakpoints[size]; | ||
| }; | ||
| export const down = (size: string) => { | ||
| validateBreakpoint(size); | ||
| return viewport.getWidth() < currentBreakpoints[size]; | ||
| }; | ||
| export const only = (size: string) => { | ||
| validateBreakpoint(size); | ||
| const width = viewport.getWidth(); | ||
| const min = currentBreakpoints[size]; | ||
| const max = getNextBreakpoint(size); | ||
| return width >= min && width < max; | ||
| }; | ||
| export const not = (size: string) => { | ||
| validateBreakpoint(size); | ||
| const width = viewport.getWidth(); | ||
| const min = currentBreakpoints[size]; | ||
| const max = getNextBreakpoint(size); | ||
| return width < min || width >= max; | ||
| }; | ||
| export const between = (minSize: string, maxSize: string) => { | ||
| validateBreakpoint(minSize); | ||
| validateBreakpoint(maxSize); | ||
| const width = viewport.getWidth(); | ||
| const min = currentBreakpoints[minSize]; | ||
| const max = currentBreakpoints[maxSize]; | ||
| return width >= min && width < max; | ||
| }; |
| export { configure, getBreakpoints, up, down, only, not, between } from './breakpoints'; | ||
| export type { Breakpoints } from './breakpoints'; | ||
| export { default as useViewport } from './useViewport'; | ||
| export { default as viewport } from './viewport'; |
| import { useEffect, useState } from 'react'; | ||
| import viewport from './viewport'; | ||
| const useViewport = () => { | ||
| const [width, setWidth] = useState(viewport.getWidth()); | ||
| useEffect(() => { | ||
| const handler = () => setWidth(viewport.getWidth()); | ||
| const removeHandler = viewport.onResize(handler); | ||
| return () => { | ||
| removeHandler(); | ||
| }; | ||
| }, []); | ||
| return width; | ||
| }; | ||
| export default useViewport; |
| class ViewportHelper { | ||
| private width: number; | ||
| private eventHandlers: (() => void)[]; | ||
| constructor() { | ||
| this.width = typeof window !== 'undefined' ? window.innerWidth : 0; | ||
| this.eventHandlers = []; | ||
| if (typeof window !== 'undefined') { | ||
| window.addEventListener('resize', this.handleResize); | ||
| } | ||
| } | ||
| private handleResize = () => { | ||
| this.width = window.innerWidth; | ||
| this.eventHandlers.forEach(handler => handler()); | ||
| }; | ||
| getWidth() { | ||
| return this.width; | ||
| } | ||
| onResize(handler: () => void) { | ||
| this.eventHandlers.push(handler); | ||
| return () => { | ||
| this.eventHandlers = this.eventHandlers.filter(h => h !== handler); | ||
| }; | ||
| } | ||
| } | ||
| const viewportHelper = new ViewportHelper(); | ||
| export default viewportHelper; |
| { | ||
| "compilerOptions": { | ||
| "target": "ES2018", | ||
| "module": "ESNext", | ||
| "moduleResolution": "node", | ||
| "lib": ["DOM", "ES2018"], | ||
| "declaration": true, | ||
| "declarationMap": true, | ||
| "sourceMap": true, | ||
| "outDir": "./dist", | ||
| "rootDir": "./src", | ||
| "strict": true, | ||
| "esModuleInterop": true, | ||
| "skipLibCheck": true, | ||
| "forceConsistentCasingInFileNames": true | ||
| }, | ||
| "include": ["src/**/*"], | ||
| "exclude": ["node_modules", "dist"] | ||
| } |
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
24044
32.24%236
26.2%22
-4.35%189
-24.1%1
Infinity%