Skip to content

Commit

Permalink
Merge pull request #13695 from TylerAPfledderer/feat/local-use-breakp…
Browse files Browse the repository at this point in the history
…oint-value

feat(hooks): build local `useBreakpointValue` and `useMediaQuery` hooks
  • Loading branch information
pettinarip authored Sep 10, 2024
2 parents 6f443d6 + 1d4ef15 commit 816648c
Show file tree
Hide file tree
Showing 6 changed files with 130 additions and 15 deletions.
2 changes: 1 addition & 1 deletion src/components/EnergyConsumptionChart/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ import ChartDataLabels from "chartjs-plugin-datalabels"
import { useRouter } from "next/router"
import { useTranslation } from "next-i18next"
import { Bar } from "react-chartjs-2"
import { useBreakpointValue } from "@chakra-ui/react"

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

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

import { useBreakpointValue } from "@/hooks/useBreakpointValue"
import useColorModeValue from "@/hooks/useColorModeValue"
import { useIsClient } from "@/hooks/useIsClient"

Expand Down
10 changes: 3 additions & 7 deletions src/components/Simulator/WalletHome/NFTList.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,12 @@
import React from "react"
import {
Box,
Flex,
type FlexProps,
Text,
useBreakpointValue,
} from "@chakra-ui/react"
import { Box, Flex, type FlexProps, Text } from "@chakra-ui/react"

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

import type { NFT } from "./interfaces"

import { useBreakpointValue } from "@/hooks/useBreakpointValue"

type NFTListProps = FlexProps & {
nfts: Array<NFT>
}
Expand Down
51 changes: 51 additions & 0 deletions src/hooks/useBreakpointValue.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import { screens } from "../../tailwind/screens"

import { useMediaQuery } from "./useMediaQuery"

const breakpointMap = {
// Essentially to query from no width if smaller than "sm"
base: "0px",
...screens,
}

type BreakpointKeys = keyof typeof breakpointMap

export const useBreakpointValue = <T = unknown>(
values: Partial<Record<BreakpointKeys, T>>,
fallbackBreakpoint?: BreakpointKeys
): T => {
const breakpointKeys = Object.keys(values) as BreakpointKeys[]

let fallbackPassed = false

const setBreakpoints = Object.entries(breakpointMap)
.map(([breakpoint, value]) => {
const item = {
breakpoint,
query: `(min-width: ${value})`,
fallback: !fallbackPassed,
}

if (breakpoint === fallbackBreakpoint) {
fallbackPassed = true
}

return item
})
.filter(({ breakpoint }) =>
breakpointKeys.includes(breakpoint as BreakpointKeys)
)

const fallbackQueries = setBreakpoints.map(({ fallback }) => fallback)

const queryValues = useMediaQuery(
setBreakpoints.map((bp) => bp.query),
fallbackQueries
)

const index = queryValues.lastIndexOf(true)

const breakpointPicked = breakpointKeys[index]

return values[breakpointPicked] as T
}
63 changes: 63 additions & 0 deletions src/hooks/useMediaQuery.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import { useState } from "react"

import { useIsomorphicLayoutEffect } from "./useIsomorphicLayoutEffect"

const IS_SERVER = typeof window === "undefined"

/**
* Modified from https://usehooks-ts.com/react-hook/use-media-query
* to account for an array of queries.
*
* Modifications sourced from Chakra-UI.
* https://github.com/chakra-ui/chakra-ui/blob/main/packages/hooks/src/use-media-query.ts
*/
export function useMediaQuery(
queries: string[],
fallbackQueries?: boolean[]
): boolean[] {
const _fallbackQueries = fallbackQueries?.filter(
(val) => val !== null
) as boolean[]

const [value, setValue] = useState(() => {
return queries.map((query, idx) => ({
media: query,
matches: !IS_SERVER
? window.matchMedia(query).matches
: !!_fallbackQueries[idx],
}))
})

useIsomorphicLayoutEffect(() => {
const matchMedias = queries.map((query) => window.matchMedia(query))

// Handles the change event of the media query.
function handleChange(evt: MediaQueryListEvent) {
setValue((prev) => {
return prev.slice().map((item) => {
if (item.media === evt.media) return { ...item, matches: evt.matches }
return item
})
})
return
}

const cleanups = matchMedias.map((v) => listen(v, handleChange))
return () => cleanups.forEach((fn) => fn())
}, [queries])

return value.map((item) => item.matches)
}

type MediaQueryCallback = (event: MediaQueryListEvent) => void

function listen(query: MediaQueryList, callback: MediaQueryCallback) {
// Use deprecated `addListener` and `removeListener` to support Safari < 14 (#135)
try {
query.addEventListener("change", callback)
return () => query.removeEventListener("change", callback)
} catch (e) {
query.addListener(callback)
return () => query.removeListener(callback)
}
}
10 changes: 3 additions & 7 deletions tailwind.config.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import type { Config } from "tailwindcss"
import plugin from "tailwindcss/plugin"

import { screens } from "./tailwind/screens"

const config = {
// TODO: Move to "class" strategy after removing Chakra
darkMode: ["selector", '[data-theme="dark"]'],
Expand All @@ -12,13 +14,7 @@ const config = {
prefix: "",
theme: {
extend: {
screens: {
sm: "480px",
md: "768px",
lg: "992px",
xl: "1280px",
"2xl": "1536px",
},
screens,
fontFamily: {
heading: "var(--font-inter)",
body: "var(--font-inter)",
Expand Down
9 changes: 9 additions & 0 deletions tailwind/screens.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import type { ScreensConfig } from "tailwindcss/types/config"

export const screens = {
sm: "480px",
md: "768px",
lg: "992px",
xl: "1280px",
"2xl": "1536px",
} satisfies ScreensConfig

0 comments on commit 816648c

Please sign in to comment.