about summary refs log tree commit diff
path: root/src/alf/index.tsx
blob: 27738e91de28d11aae4f62b0dfcf5483fdfacfab (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
import React from 'react'
import {Dimensions} from 'react-native'
import * as themes from '#/alf/themes'

export * from '#/alf/types'
export * as tokens from '#/alf/tokens'
export {atoms} from '#/alf/atoms'
export * from '#/alf/util/platform'
export * from '#/alf/util/flatten'

type BreakpointName = keyof typeof breakpoints

/*
 * Breakpoints
 */
const breakpoints: {
  [key: string]: number
} = {
  gtMobile: 800,
  gtTablet: 1300,
}
function getActiveBreakpoints({width}: {width: number}) {
  const active: (keyof typeof breakpoints)[] = Object.keys(breakpoints).filter(
    breakpoint => width >= breakpoints[breakpoint],
  )

  return {
    active: active[active.length - 1],
    gtMobile: active.includes('gtMobile'),
    gtTablet: active.includes('gtTablet'),
  }
}

/*
 * Context
 */
export const Context = React.createContext<{
  themeName: themes.ThemeName
  theme: themes.Theme
  breakpoints: {
    active: BreakpointName | undefined
    gtMobile: boolean
    gtTablet: boolean
  }
}>({
  themeName: 'light',
  theme: themes.light,
  breakpoints: {
    active: undefined,
    gtMobile: false,
    gtTablet: false,
  },
})

export function ThemeProvider({
  children,
  theme: themeName,
}: React.PropsWithChildren<{theme: themes.ThemeName}>) {
  const theme = themes[themeName]
  const [breakpoints, setBreakpoints] = React.useState(() =>
    getActiveBreakpoints({width: Dimensions.get('window').width}),
  )

  React.useEffect(() => {
    const listener = Dimensions.addEventListener('change', ({window}) => {
      const bp = getActiveBreakpoints({width: window.width})
      if (bp.active !== breakpoints.active) setBreakpoints(bp)
    })

    return listener.remove
  }, [breakpoints, setBreakpoints])

  return (
    <Context.Provider
      value={React.useMemo(
        () => ({
          themeName: themeName,
          theme: theme,
          breakpoints,
        }),
        [theme, themeName, breakpoints],
      )}>
      {children}
    </Context.Provider>
  )
}

export function useTheme() {
  return React.useContext(Context).theme
}

export function useBreakpoints() {
  return React.useContext(Context).breakpoints
}