Themes.purs 3.51 KB
Newer Older
1 2
module Gargantext.Components.Themes where

3 4 5
import Gargantext.Prelude

import DOM.Simple (document)
6
import Data.Array as A
7
import Data.Eq.Generic (genericEq)
8
import Data.Generic.Rep (class Generic)
9
import Data.Maybe (Maybe(..))
10
import Data.Nullable (toMaybe)
11
import Effect (Effect)
12 13
import FFI.Simple ((...), (.=))
import Gargantext.Utils.Reactix as R2
14 15
import Reactix as R
import Reactix.DOM.HTML as H
16
import Toestand as T
17

18 19 20 21 22 23 24 25 26 27 28 29 30 31
-- (?) Unknown runtime DOM errors lead to a FFI workaround for setting the
--     property of the element (see `markThemeToDOMTree` method)
--
--     Both use cases throw the error:
--
--       ```
--       TypeError: FFI_Simple_Functions.applyMethod'(...)(...)(...) is not a function
--       ```
--
--       ```purescript
--        _ <- el ... "setAttribute" $ [ "data-theme", name ]
--        _ <- pure $ (el .= "data-theme") name
--       ```
foreign import setAttribute :: R.Element -> String -> String -> Effect Unit
32

33 34
here :: R2.Here
here = R2.here "Gargantext.Components.Themes"
35 36 37 38

stylesheetElId :: String
stylesheetElId = "bootstrap-css"

39 40
newtype Theme = Theme { location :: String
                      , name :: String }
41 42
derive instance Generic Theme _
instance Eq Theme where
43
  eq = genericEq
44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59

themeName :: Theme -> String
themeName (Theme { name }) = name

defaultTheme :: Theme
defaultTheme = Theme { name: "default"
                     , location: "styles/bootstrap-default.css" }

greysonTheme :: Theme
greysonTheme = Theme { name: "greyson"
                     , location: "styles/bootstrap-greyson.css" }

monotonyTheme :: Theme
monotonyTheme = Theme { name: "monotony"
                      , location: "styles/bootstrap-monotony.css" }

60 61 62 63 64
herbieTheme :: Theme
herbieTheme = Theme { name: "herbie"
                      , location: "styles/bootstrap-herbie.css" }

darksterTheme :: Theme
65
darksterTheme = Theme { name: "darkster"
66 67
                      , location: "styles/bootstrap-darkster.css" }

68
allThemes :: Array Theme
69
allThemes = [ defaultTheme, greysonTheme, monotonyTheme, herbieTheme, darksterTheme]
70 71 72 73 74 75 76 77 78 79

switchTheme :: Theme -> Effect Unit
switchTheme (Theme { location }) = do
  mEl <- R2.getElementById stylesheetElId
  case mEl of
    Nothing -> pure unit
    Just el -> do
      _ <- pure $ (el .= "href") location
      pure unit

80 81 82 83 84 85 86 87
markThemeToDOMTree :: Theme -> Effect Unit
markThemeToDOMTree (Theme { name }) = do
  mEl <- pure $ toMaybe (document ... "getElementById" $ [ "app" ])
  case mEl of
    Nothing -> pure unit
    Just el -> setAttribute el "data-theme" name


88
type ThemeSwitcherProps = (
89
    theme  :: T.Box Theme
90 91 92 93 94 95 96
  , themes :: Array Theme
  )

themeSwitcher :: R2.Component ThemeSwitcherProps
themeSwitcher = R.createElement themeSwitcherCpt

themeSwitcherCpt :: R.Component ThemeSwitcherProps
97
themeSwitcherCpt = here.component "themeSwitcher" cpt
98 99
  where
    cpt { theme, themes } _ = do
100
      currentTheme <- T.useLive T.unequal theme
101 102 103 104

      let option (Theme { name }) = H.option { value: name } [ H.text name ]
      let options = map option themes

105
      R.useEffectOnce' $ markThemeToDOMTree currentTheme
106

107
      pure $ R2.select { className: "form-control"
108 109
                       , defaultValue: themeName currentTheme
                       , on: { change: onChange theme } } options
110
      where
111
        onChange box e = do
112 113 114 115 116 117 118
          let value = R.unsafeEventValue e
          let mTheme = A.head $ A.filter (\(Theme { name }) -> value == name) themes

          case mTheme of
            Nothing -> pure unit
            Just t  -> do
              switchTheme t
119
              markThemeToDOMTree t
120
              T.write_ t box