UNPKG

3.56 kBJavaScriptView Raw
1import useMediaQuery from './useMediaQuery';
2import { useMemo } from 'react';
3/**
4 * Create a responsive hook we a set of breakpoint names and widths.
5 * You can use any valid css units as well as a numbers (for pixels).
6 *
7 * **NOTE:** The object key order is important! it's assumed to be in order from smallest to largest
8 *
9 * ```ts
10 * const useBreakpoint = createBreakpointHook({
11 * xs: 0,
12 * sm: 576,
13 * md: 768,
14 * lg: 992,
15 * xl: 1200,
16 * })
17 * ```
18 *
19 * **Watch out!** using string values will sometimes construct media queries using css `calc()` which
20 * is NOT supported in media queries by all browsers at the moment. use numbers for
21 * the widest range of browser support.
22 *
23 * @param breakpointValues A object hash of names to breakpoint dimensions
24 */
25export function createBreakpointHook(breakpointValues) {
26 const names = Object.keys(breakpointValues);
27 function and(query, next) {
28 if (query === next) {
29 return next;
30 }
31 return query ? `${query} and ${next}` : next;
32 }
33 function getNext(breakpoint) {
34 return names[Math.min(names.indexOf(breakpoint) + 1, names.length - 1)];
35 }
36 function getMaxQuery(breakpoint) {
37 const next = getNext(breakpoint);
38 let value = breakpointValues[next];
39 if (typeof value === 'number') value = `${value - 0.2}px`;else value = `calc(${value} - 0.2px)`;
40 return `(max-width: ${value})`;
41 }
42 function getMinQuery(breakpoint) {
43 let value = breakpointValues[breakpoint];
44 if (typeof value === 'number') {
45 value = `${value}px`;
46 }
47 return `(min-width: ${value})`;
48 }
49
50 /**
51 * Match a set of breakpoints
52 *
53 * ```tsx
54 * const MidSizeOnly = () => {
55 * const isMid = useBreakpoint({ lg: 'down', sm: 'up' });
56 *
57 * if (isMid) return <div>On a Reasonable sized Screen!</div>
58 * return null;
59 * }
60 * ```
61 * @param breakpointMap An object map of breakpoints and directions, queries are constructed using "and" to join
62 * breakpoints together
63 * @param window Optionally specify the target window to match against (useful when rendering into iframes)
64 */
65
66 /**
67 * Match a single breakpoint exactly, up, or down.
68 *
69 * ```tsx
70 * const PhoneOnly = () => {
71 * const isSmall = useBreakpoint('sm', 'down');
72 *
73 * if (isSmall) return <div>On a Small Screen!</div>
74 * return null;
75 * }
76 * ```
77 *
78 * @param breakpoint The breakpoint key
79 * @param direction A direction 'up' for a max, 'down' for min, true to match only the breakpoint
80 * @param window Optionally specify the target window to match against (useful when rendering into iframes)
81 */
82
83 function useBreakpoint(breakpointOrMap, direction, window) {
84 let breakpointMap;
85 if (typeof breakpointOrMap === 'object') {
86 breakpointMap = breakpointOrMap;
87 window = direction;
88 direction = true;
89 } else {
90 direction = direction || true;
91 breakpointMap = {
92 [breakpointOrMap]: direction
93 };
94 }
95 let query = useMemo(() => Object.entries(breakpointMap).reduce((query, [key, direction]) => {
96 if (direction === 'up' || direction === true) {
97 query = and(query, getMinQuery(key));
98 }
99 if (direction === 'down' || direction === true) {
100 query = and(query, getMaxQuery(key));
101 }
102 return query;
103 }, ''), [JSON.stringify(breakpointMap)]);
104 return useMediaQuery(query, window);
105 }
106 return useBreakpoint;
107}
108const useBreakpoint = createBreakpointHook({
109 xs: 0,
110 sm: 576,
111 md: 768,
112 lg: 992,
113 xl: 1200,
114 xxl: 1400
115});
116export default useBreakpoint;
\No newline at end of file