breakpoint-utils 1.0.0 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -11,63 +11,39 @@ npm install breakpoint-utils
11
11
  ## Features
12
12
 
13
13
  - **Configurable breakpoints** - Use defaults or define your own
14
- - **Responsive utilities** - `up`, `down`, `only`, `not`, `between`
15
- - **React hook** - `useViewport` for reactive viewport width
14
+ - **Imperative API** - `up`, `down`, `only`, `not`, `between` for use anywhere
15
+ - **React hooks** - `useUp`, `useDown`, `useOnly`, `useNot`, `useBetween` for reactive components
16
+ - **Viewport measurements** - `useViewport` for pixel-based calculations
17
+ - **CSS parity** - Uses `matchMedia` for exact CSS behavior
16
18
  - **Lightweight** - Minimal dependencies
17
19
  - **TypeScript** - Full type safety
18
20
 
19
- ## Quick Start
21
+ ## Default Breakpoints
20
22
 
21
- ### Default Breakpoints
23
+ | Name | Min Width |
24
+ |------|-----------|
25
+ | `xs` | 480px |
26
+ | `sm` | 640px |
27
+ | `md` | 768px |
28
+ | `lg` | 1024px |
29
+ | `xl` | 1280px |
30
+ | `2xl` | 1536px |
22
31
 
23
- ```typescript
24
- import { up, down, only } from 'breakpoint-utils';
25
-
26
- // Default breakpoints: xs: 480, sm: 640, md: 768, lg: 1024, xl: 1280, 2xl: 1536
32
+ ## Configuration
27
33
 
28
- if (up('md')) {
29
- // viewport >= 768px
30
- }
31
-
32
- if (down('lg')) {
33
- // viewport < 1024px
34
- }
35
-
36
- if (only('sm')) {
37
- // 640px <= viewport < 768px
38
- }
39
- ```
34
+ ### `configure(breakpoints)`
40
35
 
41
- ### Custom Breakpoints
36
+ Replace the default breakpoints with your own. Must be called before any components mount.
42
37
 
43
38
  ```typescript
44
- import { configure, up, down } from 'breakpoint-utils';
39
+ import { configure } from 'breakpoint-utils';
45
40
 
46
- // Define your own breakpoints
47
41
  configure({
48
42
  mobile: 320,
49
43
  tablet: 768,
50
44
  desktop: 1024,
51
45
  wide: 1440,
52
46
  });
53
-
54
- if (up('tablet')) {
55
- // viewport >= 768px
56
- }
57
- ```
58
-
59
- ## API
60
-
61
- ### `configure(breakpoints)`
62
-
63
- Set custom breakpoints. Must be called before using utility functions.
64
-
65
- ```typescript
66
- configure({
67
- small: 600,
68
- medium: 900,
69
- large: 1200,
70
- });
71
47
  ```
72
48
 
73
49
  ### `getBreakpoints()`
@@ -79,20 +55,28 @@ const breakpoints = getBreakpoints();
79
55
  // { xs: 480, sm: 640, ... } or your custom config
80
56
  ```
81
57
 
58
+ ## Imperative API
59
+
60
+ Use these in event handlers, utility functions, or anywhere outside of React rendering.
61
+
62
+ All functions accept a named breakpoint string. `up`, `down`, and `between` also accept a number for ad-hoc pixel values.
63
+
82
64
  ### `up(size)`
83
65
 
84
66
  Returns `true` if viewport width is >= the breakpoint.
85
67
 
86
68
  ```typescript
87
- up('md') // viewport >= 768px
69
+ up('md') // viewport >= 768px
70
+ up(900) // viewport >= 900px
88
71
  ```
89
72
 
90
73
  ### `down(size)`
91
74
 
92
- Returns `true` if viewport width is < the breakpoint.
75
+ Returns `true` if viewport is **below** the breakpoint (does not include it).
93
76
 
94
77
  ```typescript
95
78
  down('lg') // viewport < 1024px
79
+ down(900) // viewport < 900px
96
80
  ```
97
81
 
98
82
  ### `only(size)`
@@ -116,50 +100,115 @@ not('md') // viewport < 768px OR viewport >= 1024px
116
100
  Returns `true` if viewport is between two breakpoints.
117
101
 
118
102
  ```typescript
119
- between('sm', 'lg') // 640px <= viewport < 1024px
103
+ between('sm', 'lg') // 640px <= viewport < 1024px
104
+ between(600, 900) // 600px <= viewport < 900px
105
+ between('sm', 900) // mix named + numeric
120
106
  ```
121
107
 
122
- ## React Hook
108
+ > **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:
109
+ >
110
+ > ```typescript
111
+ > import { up, viewport } from 'breakpoint-utils';
112
+ >
113
+ > // viewport exposes getWidth() and onResize() for manual subscription
114
+ > const unsubscribe = viewport.onResize(() => {
115
+ > console.log('Is desktop:', up('lg'));
116
+ > });
117
+ >
118
+ > // Clean up when no longer needed
119
+ > unsubscribe();
120
+ > ```
123
121
 
124
- ### `useViewport()`
122
+ ## React Hooks
123
+
124
+ 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.
125
125
 
126
- React hook that returns the current viewport width and updates on resize.
126
+ ### `useUp(size)`
127
+
128
+ Returns `true` if viewport width is >= the breakpoint.
127
129
 
128
130
  ```typescript
129
- import { useViewport, configure, up } from 'breakpoint-utils';
131
+ const isDesktop = useUp('lg'); // viewport >= 1024px
132
+ const isWide = useUp(900); // viewport >= 900px
133
+ ```
130
134
 
131
- // Optional: configure custom breakpoints
132
- configure({
133
- mobile: 375,
134
- tablet: 768,
135
- desktop: 1024,
136
- });
135
+ ### `useDown(size)`
137
136
 
138
- function MyComponent() {
137
+ Returns `true` if viewport is **below** the breakpoint (does not include it).
138
+
139
+ ```typescript
140
+ const isMobile = useDown('md'); // viewport < 768px
141
+ const isNarrow = useDown(600); // viewport < 600px
142
+ ```
143
+
144
+ ### `useOnly(size)`
145
+
146
+ Returns `true` if viewport is within the breakpoint range.
147
+
148
+ ```typescript
149
+ const isTabletOnly = useOnly('md'); // 768px <= viewport < 1024px
150
+ ```
151
+
152
+ ### `useNot(size)`
153
+
154
+ Returns `true` if viewport is outside the breakpoint range.
155
+
156
+ ```typescript
157
+ const isNotTablet = useNot('md'); // viewport < 768px OR viewport >= 1024px
158
+ ```
159
+
160
+ ### `useBetween(minSize, maxSize)`
161
+
162
+ Returns `true` if viewport is between two breakpoints.
163
+
164
+ ```typescript
165
+ const isSmallToMedium = useBetween('sm', 'lg'); // 640px <= viewport < 1024px
166
+ const isCustomRange = useBetween(600, 900); // 600px <= viewport < 900px
167
+ ```
168
+
169
+ > **SSR Note:** All hooks return `false` during server-side rendering until hydration completes.
170
+
171
+ ### `useViewport()`
172
+
173
+ Returns the current viewport width in pixels. Use for pixel-based calculations (drag/resize logic, canvas, charts, virtualization):
174
+
175
+ ```typescript
176
+ import { useViewport } from 'breakpoint-utils';
177
+
178
+ function ResizablePanel() {
139
179
  const width = useViewport();
140
-
141
- return (
142
- <div>
143
- <p>Viewport width: {width}px</p>
144
- {up('tablet') && <p>Tablet or larger</p>}
145
- </div>
146
- );
180
+ const panelWidth = Math.min(width * 0.8, 600);
181
+
182
+ return <div style={{ width: panelWidth }} />;
147
183
  }
148
184
  ```
149
185
 
150
- ## Direct Viewport Access
186
+ > **Note:** Use breakpoint hooks (`useUp`, etc.) for responsive layouts. Use `useViewport` only when you need actual pixel values for calculations.
187
+
188
+ ## Migrating from v1.0.0
189
+
190
+ In v1.0.0, reactive breakpoint checks required `useViewport()` to trigger re-renders:
151
191
 
152
192
  ```typescript
153
- import { viewport } from 'breakpoint-utils';
193
+ import { useViewport, up } from 'breakpoint-utils';
154
194
 
155
- const width = viewport.getWidth();
195
+ function MyComponent() {
196
+ useViewport(); // re-renders on every pixel change
197
+ const isDesktop = up('lg');
198
+ return isDesktop ? <DesktopNav /> : <MobileNav />;
199
+ }
200
+ ```
156
201
 
157
- const unsubscribe = viewport.onResize(() => {
158
- console.log('Viewport resized:', viewport.getWidth());
159
- });
202
+ This still works, but the dedicated hooks are preferred — they only re-render when a breakpoint boundary is crossed:
160
203
 
161
- // Clean up
162
- unsubscribe();
204
+ ```typescript
205
+ // v2.0.0+ — dedicated hooks
206
+ import { useUp } from 'breakpoint-utils';
207
+
208
+ function MyComponent() {
209
+ const isDesktop = useUp('lg'); // re-renders only at 1024px boundary
210
+ return isDesktop ? <DesktopNav /> : <MobileNav />;
211
+ }
163
212
  ```
164
213
 
165
214
  ## TypeScript
@@ -1,9 +1,23 @@
1
1
  export type Breakpoints = Record<string, number>;
2
+ export type BreakpointValue = string | number;
3
+ /**
4
+ * Set custom breakpoints. Call this at app startup before any components mount.
5
+ * Calling after mount will not update active hook subscriptions until the next re-render.
6
+ */
2
7
  export declare const configure: <T extends Breakpoints>(customBreakpoints: T) => void;
3
8
  export declare const getBreakpoints: () => Breakpoints;
4
- export declare const up: (size: string) => boolean;
5
- export declare const down: (size: string) => boolean;
9
+ /**
10
+ * @internal Query builders - used by hook layer, not part of public API
11
+ */
12
+ declare const buildUpQuery: (size: BreakpointValue) => string;
13
+ declare const buildDownQuery: (size: BreakpointValue) => string;
14
+ declare const buildOnlyQuery: (size: string) => string;
15
+ declare const buildNotQuery: (size: string) => string;
16
+ declare const buildBetweenQuery: (minSize: BreakpointValue, maxSize: BreakpointValue) => string;
17
+ export declare const up: (size: BreakpointValue) => boolean;
18
+ export declare const down: (size: BreakpointValue) => boolean;
6
19
  export declare const only: (size: string) => boolean;
7
20
  export declare const not: (size: string) => boolean;
8
- export declare const between: (minSize: string, maxSize: string) => boolean;
21
+ export declare const between: (minSize: BreakpointValue, maxSize: BreakpointValue) => boolean;
22
+ export { buildUpQuery, buildDownQuery, buildOnlyQuery, buildNotQuery, buildBetweenQuery };
9
23
  //# sourceMappingURL=breakpoints.d.ts.map
@@ -1 +1 @@
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"}
1
+ {"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"}
@@ -1,4 +1,3 @@
1
- import viewport from './viewport';
2
1
  const defaultBreakpoints = {
3
2
  xs: 480,
4
3
  sm: 640,
@@ -13,6 +12,10 @@ const updateSortedBreakpoints = () => {
13
12
  sortedBreakpoints = Object.entries(currentBreakpoints).sort((a, b) => a[1] - b[1]);
14
13
  };
15
14
  updateSortedBreakpoints();
15
+ /**
16
+ * Set custom breakpoints. Call this at app startup before any components mount.
17
+ * Calling after mount will not update active hook subscriptions until the next re-render.
18
+ */
16
19
  export const configure = (customBreakpoints) => {
17
20
  currentBreakpoints = customBreakpoints;
18
21
  updateSortedBreakpoints();
@@ -23,39 +26,61 @@ const validateBreakpoint = (size) => {
23
26
  throw new Error(`Invalid breakpoint: "${size}". Available breakpoints: ${Object.keys(currentBreakpoints).join(', ')}`);
24
27
  }
25
28
  };
29
+ const resolveSize = (size) => {
30
+ if (typeof size === 'number')
31
+ return size;
32
+ validateBreakpoint(size);
33
+ return currentBreakpoints[size];
34
+ };
26
35
  const getNextBreakpoint = (size) => {
27
36
  const currentIndex = sortedBreakpoints.findIndex(([name]) => name === size);
28
37
  const next = sortedBreakpoints[currentIndex + 1];
29
38
  return next ? next[1] : Infinity;
30
39
  };
31
- export const up = (size) => {
32
- validateBreakpoint(size);
33
- return viewport.getWidth() >= currentBreakpoints[size];
40
+ /**
41
+ * @internal Query builders - used by hook layer, not part of public API
42
+ */
43
+ const buildUpQuery = (size) => {
44
+ return `(min-width: ${resolveSize(size)}px)`;
34
45
  };
35
- export const down = (size) => {
36
- validateBreakpoint(size);
37
- return viewport.getWidth() < currentBreakpoints[size];
46
+ const buildDownQuery = (size) => {
47
+ return `(max-width: ${resolveSize(size) - 0.02}px)`;
38
48
  };
39
- export const only = (size) => {
49
+ const buildOnlyQuery = (size) => {
40
50
  validateBreakpoint(size);
41
- const width = viewport.getWidth();
42
51
  const min = currentBreakpoints[size];
43
52
  const max = getNextBreakpoint(size);
44
- return width >= min && width < max;
53
+ return max === Infinity
54
+ ? `(min-width: ${min}px)`
55
+ : `(min-width: ${min}px) and (max-width: ${max - 0.02}px)`;
45
56
  };
46
- export const not = (size) => {
57
+ const buildNotQuery = (size) => {
47
58
  validateBreakpoint(size);
48
- const width = viewport.getWidth();
49
59
  const min = currentBreakpoints[size];
50
60
  const max = getNextBreakpoint(size);
51
- return width < min || width >= max;
61
+ return max === Infinity
62
+ ? `(max-width: ${min - 0.02}px)`
63
+ : `(max-width: ${min - 0.02}px), (min-width: ${max}px)`;
64
+ };
65
+ const buildBetweenQuery = (minSize, maxSize) => {
66
+ const min = resolveSize(minSize);
67
+ const max = resolveSize(maxSize);
68
+ if (min >= max) {
69
+ throw new Error(`minSize "${minSize}" (${min}px) must be smaller than maxSize "${maxSize}" (${max}px)`);
70
+ }
71
+ return `(min-width: ${min}px) and (max-width: ${max - 0.02}px)`;
52
72
  };
53
- export const between = (minSize, maxSize) => {
54
- validateBreakpoint(minSize);
55
- validateBreakpoint(maxSize);
56
- const width = viewport.getWidth();
57
- const min = currentBreakpoints[minSize];
58
- const max = currentBreakpoints[maxSize];
59
- return width >= min && width < max;
73
+ // Imperative API (use in handlers, utils, non-React code)
74
+ const matchMedia = (query) => {
75
+ if (typeof window === 'undefined')
76
+ return false;
77
+ return window.matchMedia(query).matches;
60
78
  };
79
+ export const up = (size) => matchMedia(buildUpQuery(size));
80
+ export const down = (size) => matchMedia(buildDownQuery(size));
81
+ export const only = (size) => matchMedia(buildOnlyQuery(size));
82
+ export const not = (size) => matchMedia(buildNotQuery(size));
83
+ export const between = (minSize, maxSize) => matchMedia(buildBetweenQuery(minSize, maxSize));
84
+ // Export query builders for hook layer
85
+ export { buildUpQuery, buildDownQuery, buildOnlyQuery, buildNotQuery, buildBetweenQuery };
61
86
  //# sourceMappingURL=breakpoints.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"breakpoints.js","sourceRoot":"","sources":["../src/breakpoints.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"breakpoints.js","sourceRoot":"","sources":["../src/breakpoints.ts"],"names":[],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  export { configure, getBreakpoints, up, down, only, not, between } from './breakpoints';
2
- export type { Breakpoints } from './breakpoints';
2
+ export type { Breakpoints, BreakpointValue } from './breakpoints';
3
+ export { useUp, useDown, useOnly, useNot, useBetween } from './useBreakpoint';
3
4
  export { default as useViewport } from './useViewport';
4
5
  export { default as viewport } from './viewport';
5
6
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
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"}
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,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"}
package/dist/index.js CHANGED
@@ -1,4 +1,5 @@
1
1
  export { configure, getBreakpoints, up, down, only, not, between } from './breakpoints';
2
+ export { useUp, useDown, useOnly, useNot, useBetween } from './useBreakpoint';
2
3
  export { default as useViewport } from './useViewport';
3
4
  export { default as viewport } from './viewport';
4
5
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
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"}
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,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"}
@@ -0,0 +1,7 @@
1
+ import { type BreakpointValue } from './breakpoints';
2
+ export declare const useUp: (size: BreakpointValue) => boolean;
3
+ export declare const useDown: (size: BreakpointValue) => boolean;
4
+ export declare const useOnly: (size: string) => boolean;
5
+ export declare const useNot: (size: string) => boolean;
6
+ export declare const useBetween: (minSize: BreakpointValue, maxSize: BreakpointValue) => boolean;
7
+ //# sourceMappingURL=useBreakpoint.d.ts.map
@@ -0,0 +1 @@
1
+ {"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"}
@@ -0,0 +1,23 @@
1
+ import { useCallback, useMemo, useSyncExternalStore } from 'react';
2
+ import { buildUpQuery, buildDownQuery, buildOnlyQuery, buildNotQuery, buildBetweenQuery, } from './breakpoints';
3
+ const useMediaQuery = (query) => {
4
+ const mql = useMemo(() => typeof window !== 'undefined' ? window.matchMedia(query) : null, [query]);
5
+ const subscribe = useCallback((callback) => {
6
+ if (!mql)
7
+ return () => { };
8
+ mql.addEventListener('change', callback);
9
+ return () => mql.removeEventListener('change', callback);
10
+ }, [mql]);
11
+ const getSnapshot = useCallback(() => {
12
+ var _a;
13
+ return (_a = mql === null || mql === void 0 ? void 0 : mql.matches) !== null && _a !== void 0 ? _a : false;
14
+ }, [mql]);
15
+ const getServerSnapshot = useCallback(() => false, []);
16
+ return useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot);
17
+ };
18
+ export const useUp = (size) => useMediaQuery(buildUpQuery(size));
19
+ export const useDown = (size) => useMediaQuery(buildDownQuery(size));
20
+ export const useOnly = (size) => useMediaQuery(buildOnlyQuery(size));
21
+ export const useNot = (size) => useMediaQuery(buildNotQuery(size));
22
+ export const useBetween = (minSize, maxSize) => useMediaQuery(buildBetweenQuery(minSize, maxSize));
23
+ //# sourceMappingURL=useBreakpoint.js.map
@@ -0,0 +1 @@
1
+ {"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"useViewport.d.ts","sourceRoot":"","sources":["../src/useViewport.ts"],"names":[],"mappings":"AAGA,QAAA,MAAM,WAAW,cAahB,CAAC;AAEF,eAAe,WAAW,CAAC"}
1
+ {"version":3,"file":"useViewport.d.ts","sourceRoot":"","sources":["../src/useViewport.ts"],"names":[],"mappings":"AAOA,QAAA,MAAM,WAAW,cAEhB,CAAC;AAEF,eAAe,WAAW,CAAC"}
@@ -1,15 +1,10 @@
1
- import { useEffect, useState } from 'react';
1
+ import { useSyncExternalStore } from 'react';
2
2
  import viewport from './viewport';
3
+ const subscribe = (callback) => viewport.onResize(callback);
4
+ const getSnapshot = () => viewport.getWidth();
5
+ const getServerSnapshot = () => 0;
3
6
  const useViewport = () => {
4
- const [width, setWidth] = useState(viewport.getWidth());
5
- useEffect(() => {
6
- const handler = () => setWidth(viewport.getWidth());
7
- const removeHandler = viewport.onResize(handler);
8
- return () => {
9
- removeHandler();
10
- };
11
- }, []);
12
- return width;
7
+ return useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot);
13
8
  };
14
9
  export default useViewport;
15
10
  //# sourceMappingURL=useViewport.js.map
@@ -1 +1 @@
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"}
1
+ {"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 @@
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"}
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,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"}
package/dist/viewport.js CHANGED
@@ -5,7 +5,7 @@ class ViewportHelper {
5
5
  this.eventHandlers.forEach(handler => handler());
6
6
  };
7
7
  this.width = typeof window !== 'undefined' ? window.innerWidth : 0;
8
- this.eventHandlers = [];
8
+ this.eventHandlers = new Set();
9
9
  if (typeof window !== 'undefined') {
10
10
  window.addEventListener('resize', this.handleResize);
11
11
  }
@@ -14,9 +14,9 @@ class ViewportHelper {
14
14
  return this.width;
15
15
  }
16
16
  onResize(handler) {
17
- this.eventHandlers.push(handler);
17
+ this.eventHandlers.add(handler);
18
18
  return () => {
19
- this.eventHandlers = this.eventHandlers.filter(h => h !== handler);
19
+ this.eventHandlers.delete(handler);
20
20
  };
21
21
  }
22
22
  }
@@ -1 +1 @@
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"}
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,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"}
package/package.json CHANGED
@@ -1,15 +1,24 @@
1
1
  {
2
2
  "name": "breakpoint-utils",
3
- "version": "1.0.0",
4
- "description": "Viewport width utility with breakpoint functions for React",
3
+ "version": "2.0.0",
4
+ "description": "Viewport width utility with optional breakpoint functions for React",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
7
+ "files": [
8
+ "dist",
9
+ "README.md"
10
+ ],
7
11
  "scripts": {
8
12
  "build": "tsc",
9
13
  "dev": "tsc --watch"
10
14
  },
11
15
  "peerDependencies": {
12
- "react": ">=16.8.0"
16
+ "react": ">=18.0.0"
17
+ },
18
+ "peerDependenciesMeta": {
19
+ "react": {
20
+ "optional": true
21
+ }
13
22
  },
14
23
  "devDependencies": {
15
24
  "@types/react": "^18.2.0",
@@ -1,75 +0,0 @@
1
- import viewport from './viewport';
2
-
3
- const defaultBreakpoints = {
4
- xs: 480,
5
- sm: 640,
6
- md: 768,
7
- lg: 1024,
8
- xl: 1280,
9
- '2xl': 1536,
10
- } as const;
11
-
12
- export type Breakpoints = Record<string, number>;
13
-
14
- let currentBreakpoints: Breakpoints = { ...defaultBreakpoints };
15
- let sortedBreakpoints: [string, number][] = [];
16
-
17
- const updateSortedBreakpoints = () => {
18
- sortedBreakpoints = Object.entries(currentBreakpoints).sort((a, b) => a[1] - b[1]);
19
- };
20
-
21
- updateSortedBreakpoints();
22
-
23
- export const configure = <T extends Breakpoints>(customBreakpoints: T) => {
24
- currentBreakpoints = customBreakpoints;
25
- updateSortedBreakpoints();
26
- };
27
-
28
- export const getBreakpoints = () => currentBreakpoints;
29
-
30
- const validateBreakpoint = (size: string): void => {
31
- if (!(size in currentBreakpoints)) {
32
- throw new Error(`Invalid breakpoint: "${size}". Available breakpoints: ${Object.keys(currentBreakpoints).join(', ')}`);
33
- }
34
- };
35
-
36
- const getNextBreakpoint = (size: string): number => {
37
- const currentIndex = sortedBreakpoints.findIndex(([name]) => name === size);
38
- const next = sortedBreakpoints[currentIndex + 1];
39
- return next ? next[1] : Infinity;
40
- };
41
-
42
- export const up = (size: string) => {
43
- validateBreakpoint(size);
44
- return viewport.getWidth() >= currentBreakpoints[size];
45
- };
46
-
47
- export const down = (size: string) => {
48
- validateBreakpoint(size);
49
- return viewport.getWidth() < currentBreakpoints[size];
50
- };
51
-
52
- export const only = (size: string) => {
53
- validateBreakpoint(size);
54
- const width = viewport.getWidth();
55
- const min = currentBreakpoints[size];
56
- const max = getNextBreakpoint(size);
57
- return width >= min && width < max;
58
- };
59
-
60
- export const not = (size: string) => {
61
- validateBreakpoint(size);
62
- const width = viewport.getWidth();
63
- const min = currentBreakpoints[size];
64
- const max = getNextBreakpoint(size);
65
- return width < min || width >= max;
66
- };
67
-
68
- export const between = (minSize: string, maxSize: string) => {
69
- validateBreakpoint(minSize);
70
- validateBreakpoint(maxSize);
71
- const width = viewport.getWidth();
72
- const min = currentBreakpoints[minSize];
73
- const max = currentBreakpoints[maxSize];
74
- return width >= min && width < max;
75
- };
package/src/index.ts DELETED
@@ -1,4 +0,0 @@
1
- export { configure, getBreakpoints, up, down, only, not, between } from './breakpoints';
2
- export type { Breakpoints } from './breakpoints';
3
- export { default as useViewport } from './useViewport';
4
- export { default as viewport } from './viewport';
@@ -1,19 +0,0 @@
1
- import { useEffect, useState } from 'react';
2
- import viewport from './viewport';
3
-
4
- const useViewport = () => {
5
- const [width, setWidth] = useState(viewport.getWidth());
6
-
7
- useEffect(() => {
8
- const handler = () => setWidth(viewport.getWidth());
9
- const removeHandler = viewport.onResize(handler);
10
-
11
- return () => {
12
- removeHandler();
13
- };
14
- }, []);
15
-
16
- return width;
17
- };
18
-
19
- export default useViewport;
package/src/viewport.ts DELETED
@@ -1,31 +0,0 @@
1
- class ViewportHelper {
2
- private width: number;
3
- private eventHandlers: (() => void)[];
4
-
5
- constructor() {
6
- this.width = typeof window !== 'undefined' ? window.innerWidth : 0;
7
- this.eventHandlers = [];
8
- if (typeof window !== 'undefined') {
9
- window.addEventListener('resize', this.handleResize);
10
- }
11
- }
12
-
13
- private handleResize = () => {
14
- this.width = window.innerWidth;
15
- this.eventHandlers.forEach(handler => handler());
16
- };
17
-
18
- getWidth() {
19
- return this.width;
20
- }
21
-
22
- onResize(handler: () => void) {
23
- this.eventHandlers.push(handler);
24
- return () => {
25
- this.eventHandlers = this.eventHandlers.filter(h => h !== handler);
26
- };
27
- }
28
- }
29
-
30
- const viewportHelper = new ViewportHelper();
31
- export default viewportHelper;
package/tsconfig.json DELETED
@@ -1,19 +0,0 @@
1
- {
2
- "compilerOptions": {
3
- "target": "ES2018",
4
- "module": "ESNext",
5
- "moduleResolution": "node",
6
- "lib": ["DOM", "ES2018"],
7
- "declaration": true,
8
- "declarationMap": true,
9
- "sourceMap": true,
10
- "outDir": "./dist",
11
- "rootDir": "./src",
12
- "strict": true,
13
- "esModuleInterop": true,
14
- "skipLibCheck": true,
15
- "forceConsistentCasingInFileNames": true
16
- },
17
- "include": ["src/**/*"],
18
- "exclude": ["node_modules", "dist"]
19
- }