Skip to content

Commit 816648c

Browse files
authored
Merge pull request #13695 from TylerAPfledderer/feat/local-use-breakpoint-value
feat(hooks): build local `useBreakpointValue` and `useMediaQuery` hooks
2 parents 6f443d6 + 1d4ef15 commit 816648c

File tree

6 files changed

+130
-15
lines changed

6 files changed

+130
-15
lines changed

src/components/EnergyConsumptionChart/index.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ import ChartDataLabels from "chartjs-plugin-datalabels"
1212
import { useRouter } from "next/router"
1313
import { useTranslation } from "next-i18next"
1414
import { Bar } from "react-chartjs-2"
15-
import { useBreakpointValue } from "@chakra-ui/react"
1615

1716
import type { Lang } from "@/lib/types"
1817

@@ -21,6 +20,7 @@ import { Center } from "@/components/ui/flex"
2120
import { wrapLabel } from "@/lib/utils/charts"
2221
import { isLangRightToLeft } from "@/lib/utils/translations"
2322

23+
import { useBreakpointValue } from "@/hooks/useBreakpointValue"
2424
import useColorModeValue from "@/hooks/useColorModeValue"
2525
import { useIsClient } from "@/hooks/useIsClient"
2626

src/components/Simulator/WalletHome/NFTList.tsx

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,12 @@
11
import React from "react"
2-
import {
3-
Box,
4-
Flex,
5-
type FlexProps,
6-
Text,
7-
useBreakpointValue,
8-
} from "@chakra-ui/react"
2+
import { Box, Flex, type FlexProps, Text } from "@chakra-ui/react"
93

104
import { Image } from "@/components/Image"
115

126
import type { NFT } from "./interfaces"
137

8+
import { useBreakpointValue } from "@/hooks/useBreakpointValue"
9+
1410
type NFTListProps = FlexProps & {
1511
nfts: Array<NFT>
1612
}

src/hooks/useBreakpointValue.ts

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
import { screens } from "../../tailwind/screens"
2+
3+
import { useMediaQuery } from "./useMediaQuery"
4+
5+
const breakpointMap = {
6+
// Essentially to query from no width if smaller than "sm"
7+
base: "0px",
8+
...screens,
9+
}
10+
11+
type BreakpointKeys = keyof typeof breakpointMap
12+
13+
export const useBreakpointValue = <T = unknown>(
14+
values: Partial<Record<BreakpointKeys, T>>,
15+
fallbackBreakpoint?: BreakpointKeys
16+
): T => {
17+
const breakpointKeys = Object.keys(values) as BreakpointKeys[]
18+
19+
let fallbackPassed = false
20+
21+
const setBreakpoints = Object.entries(breakpointMap)
22+
.map(([breakpoint, value]) => {
23+
const item = {
24+
breakpoint,
25+
query: `(min-width: ${value})`,
26+
fallback: !fallbackPassed,
27+
}
28+
29+
if (breakpoint === fallbackBreakpoint) {
30+
fallbackPassed = true
31+
}
32+
33+
return item
34+
})
35+
.filter(({ breakpoint }) =>
36+
breakpointKeys.includes(breakpoint as BreakpointKeys)
37+
)
38+
39+
const fallbackQueries = setBreakpoints.map(({ fallback }) => fallback)
40+
41+
const queryValues = useMediaQuery(
42+
setBreakpoints.map((bp) => bp.query),
43+
fallbackQueries
44+
)
45+
46+
const index = queryValues.lastIndexOf(true)
47+
48+
const breakpointPicked = breakpointKeys[index]
49+
50+
return values[breakpointPicked] as T
51+
}

src/hooks/useMediaQuery.ts

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
import { useState } from "react"
2+
3+
import { useIsomorphicLayoutEffect } from "./useIsomorphicLayoutEffect"
4+
5+
const IS_SERVER = typeof window === "undefined"
6+
7+
/**
8+
* Modified from https://usehooks-ts.com/react-hook/use-media-query
9+
* to account for an array of queries.
10+
*
11+
* Modifications sourced from Chakra-UI.
12+
* https://github.com/chakra-ui/chakra-ui/blob/main/packages/hooks/src/use-media-query.ts
13+
*/
14+
export function useMediaQuery(
15+
queries: string[],
16+
fallbackQueries?: boolean[]
17+
): boolean[] {
18+
const _fallbackQueries = fallbackQueries?.filter(
19+
(val) => val !== null
20+
) as boolean[]
21+
22+
const [value, setValue] = useState(() => {
23+
return queries.map((query, idx) => ({
24+
media: query,
25+
matches: !IS_SERVER
26+
? window.matchMedia(query).matches
27+
: !!_fallbackQueries[idx],
28+
}))
29+
})
30+
31+
useIsomorphicLayoutEffect(() => {
32+
const matchMedias = queries.map((query) => window.matchMedia(query))
33+
34+
// Handles the change event of the media query.
35+
function handleChange(evt: MediaQueryListEvent) {
36+
setValue((prev) => {
37+
return prev.slice().map((item) => {
38+
if (item.media === evt.media) return { ...item, matches: evt.matches }
39+
return item
40+
})
41+
})
42+
return
43+
}
44+
45+
const cleanups = matchMedias.map((v) => listen(v, handleChange))
46+
return () => cleanups.forEach((fn) => fn())
47+
}, [queries])
48+
49+
return value.map((item) => item.matches)
50+
}
51+
52+
type MediaQueryCallback = (event: MediaQueryListEvent) => void
53+
54+
function listen(query: MediaQueryList, callback: MediaQueryCallback) {
55+
// Use deprecated `addListener` and `removeListener` to support Safari < 14 (#135)
56+
try {
57+
query.addEventListener("change", callback)
58+
return () => query.removeEventListener("change", callback)
59+
} catch (e) {
60+
query.addListener(callback)
61+
return () => query.removeListener(callback)
62+
}
63+
}

tailwind.config.ts

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import type { Config } from "tailwindcss"
22
import plugin from "tailwindcss/plugin"
33

4+
import { screens } from "./tailwind/screens"
5+
46
const config = {
57
// TODO: Move to "class" strategy after removing Chakra
68
darkMode: ["selector", '[data-theme="dark"]'],
@@ -12,13 +14,7 @@ const config = {
1214
prefix: "",
1315
theme: {
1416
extend: {
15-
screens: {
16-
sm: "480px",
17-
md: "768px",
18-
lg: "992px",
19-
xl: "1280px",
20-
"2xl": "1536px",
21-
},
17+
screens,
2218
fontFamily: {
2319
heading: "var(--font-inter)",
2420
body: "var(--font-inter)",

tailwind/screens.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import type { ScreensConfig } from "tailwindcss/types/config"
2+
3+
export const screens = {
4+
sm: "480px",
5+
md: "768px",
6+
lg: "992px",
7+
xl: "1280px",
8+
"2xl": "1536px",
9+
} satisfies ScreensConfig

0 commit comments

Comments
 (0)