ECharts.purs 8.7 KB
Newer Older
Sudhir Kumar's avatar
Sudhir Kumar committed
1
module Gargantext.Components.Charts.Options.ECharts where
2 3

import CSS.Common (normal)
4
import CSS.FontStyle (FontStyle(..))
5 6 7
import Data.Maybe (Maybe(..))
import Data.Nullable (toMaybe)
import Effect (Effect)
8
import Gargantext.Components.Charts.Options.Color (transparent, violet, black)
9 10
import Gargantext.Components.Charts.Options.Data (DataLegend, dataSerie)
import Gargantext.Components.Charts.Options.Font (IconOptions(..), Shape(..), TextStyle, chartFontStyle, chartFontWeight, icon, mkTooltip, Tooltip, mkToolBox)
Sudhir Kumar's avatar
Sudhir Kumar committed
11 12
import Gargantext.Components.Charts.Options.Legend (legendType, LegendMode(..), PlainOrScroll(..), selectedMode, Orientation(..), orient)
import Gargantext.Components.Charts.Options.Position (Align(..), LeftRelativePosition(..), TopRelativePosition(..), numberPosition, percentPosition, relativePosition)
13
import Gargantext.Components.Charts.Options.Series (Series, seriesPieD1)
14 15
import Gargantext.Components.Charts.Options.Type (DataZoom, EChartsInstance, Echarts, Legend, MouseEvent, Option, Title, XAxis, YAxis, EChartRef, xAxis, yAxis)
import Gargantext.Utils.Reactix as R2
16
import Prelude
17 18
import React (ReactClass, unsafeCreateElementDynamic)
import Reactix as R
19
import Record.Extra as RX
Sudhir Kumar's avatar
Sudhir Kumar committed
20
import Unsafe.Coerce (unsafeCoerce)
Sudhir Kumar's avatar
Sudhir Kumar committed
21

22
foreign import eChartsClass :: ReactClass Echarts
23
foreign import listenerFn1 :: forall evt. (evt -> Effect Unit) -> Effect Unit
arturo's avatar
arturo committed
24
-- | https://echarts.apache.org/v4/en/api.html#echartsInstance.dispatchAction
25
foreign import dispatchAction :: forall payload. EChartsInstance -> payload -> Effect Unit
26

27
chart :: Options -> R.Element
28
chart = echarts <<< chartWith
29

30 31 32
chartWith :: Options -> Echarts
chartWith options =
  { option    : opts options
33 34 35 36 37 38 39 40 41 42 43
--, className : Nothing
--, style     : Nothing
--, theme     : Nothing
--, group     : Nothing
--, initOpts  : Nothing
--, notMerge  : Nothing
--, lazyUpdate: Nothing
--, loading   : Nothing
--, optsLoading: Nothing
--, onReady    : Nothing
--, resizable  : Nothing
44 45
  , onEvents  : getEvents options
  , ref       : refListener options
46
  }
47 48 49 50 51 52 53 54 55 56
    where
      getEvents (Options { onClick }) =
        { click: listenerFn1 \e -> case onClick of
            -- sanitize parsing (see MouseEvent comment)
            Just fn -> RX.pick (e :: MouseEvent) # fn
            Nothing -> pure unit
        }

      refListener (Options { onInit }) = case onInit of
        Nothing -> pure unit
57
        Just fn -> listenerFn1 (execOnInit fn)
58 59 60

      execOnInit fn = toMaybe >>> case _ of
        Nothing                        -> pure unit
arturo's avatar
arturo committed
61 62 63 64 65
        -- Just (ref :: Record EChartRef) -> fn =<< ref.getEchartsInstance
        -- ^ this line can break for some reasons... (see Issue #312)
        Just (ref :: Record EChartRef) -> do
          i <- ref.getEchartsInstance
          fn i
66

67 68
echarts :: Echarts -> R.Element
echarts c = R2.buff $ unsafeCreateElementDynamic (unsafeCoerce eChartsClass) c []
69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89

type MainTitle = String
type SubTitle  = String

title :: MainTitle -> SubTitle -> Title
title mainTitle subTitle =
  {
    id: ""
   ,show: true
   ,text: mainTitle
   ,link: ""
   ,target: "blank"
   ,textStyle: textStyle
   ,subtext: subTitle
   ,sublink: ""
   ,subtarget: "blank"
   ,subtextStyle: textStyle2
   ,padding: 10.0
   ,itemGap: 0.0
   ,zlevel: 2.0
   ,z: 2.0
90
   ,left: relativePosition (Relative RightPos)
91 92 93
   ,top: relativePosition (Relative Top)
   ,right: numberPosition 60.0
   ,bottom: percentPosition 40.0
94 95
   ,backgroundColor: transparent
   ,borderColor: transparent
96 97 98
   ,borderWidth: 0.0
   ,borderRadius: 0.0
   ,shadowBlur: 0.0
99
   ,shadowColor: transparent
100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123
   ,shadowOffsetX: 0.0
   ,shadowOffsetY: 0.0
  }

legend :: Legend
legend =
  {
    id: "Muda"
   ,"type": legendType Plain
   , show: true
   , zlevel: 0.0
   , z: 2.0
   , left: relativePosition Auto
   , top: relativePosition Auto
   , right: relativePosition Auto
   , bottom: relativePosition Auto
   , width: relativePosition Auto
   , height: relativePosition Auto
   , orient: orient Horizontal
   , align: relativePosition Auto
   , padding: 5.0
   , itemGap: 10.0
   , itemWidth: 25.0
   , itemHeight: 14.0
124
 --, formatter: Nothing
125
   , selectedMode: selectedMode $ Bool true
126
   , inactiveColor: violet
127
 --- selected: Nothing
128 129 130 131
   , textStyle: textStyle
   , "data": [data1]
  }

132
data1 :: DataLegend
133 134
data1 = {name: "Map terms coverage", icon: icon $ Shape Circle, textStyle: textStyle'}

135
data2 :: DataLegend
136 137
data2 = {name: "Favorites", icon: icon $ Shape Circle, textStyle: textStyle'}

138
data3 :: DataLegend
139 140
data3 = {name: "Test", icon: icon $ Shape Diamond, textStyle: textStyle'}

141

142 143 144
yAxisVoid :: YAxis
yAxisVoid = yAxis
  { "type": ""
145 146 147 148 149 150 151
  , name: ""
  , min: 0
  , position: ""
  , axisLabel: {formatter: ""}
  , show: false
  }

152
xAxis' :: Array String -> XAxis
153
xAxis' [] = unsafeCoerce {show:false}
154
xAxis' xs = xAxis
155
            { "data": xs
156 157
            , "type": "category"
            , axisTick: {alignWithLabel: true}
158
            , show: true
159
            , axisLabel: {formatter: "{value}"}
160 161 162 163 164
            }

-- TODO try to use Optional
yAxis' :: { position :: String
          , show     :: Boolean
165
          , min      :: Int
166
          } -> YAxis
167
yAxis' {position, show, min} = yAxis
168
  { "type": "value"
169
  , name: ""
170
  , min: min
171 172 173
  , axisLabel: {formatter: "{value}"}
  , position
  , show
174 175
  }

176 177 178 179 180 181 182
data Options = Options
  { mainTitle :: MainTitle
  , subTitle  :: SubTitle
  , xAxis     :: XAxis
  , yAxis     :: YAxis
  , series    :: Array Series
  , addZoom   :: Boolean
183
  , tooltip   :: Tooltip
184 185 186 187 188 189 190 191 192 193 194 195
  , onClick   :: Maybe (MouseEvent -> Effect Unit)
  -- (?) `onInit` custom listener
  --
  --      * in addition of the already existing `onReady` native listener
  --        which is executed on chart mount, but does not provide any arg
  --      * the React library also contained another native listener as
  --        `ref`, which adds the React Ref of the mounted chart
  --      * this additional `onInit` is executed after the "Apache Echarts"
  --        has been "initialised" (see more details [1]),
  --        it intends to return the `eChartsInstance` used for every
  --        library actions
  --
arturo's avatar
arturo committed
196
  -- [1] https://echarts.apache.org/v4/en/api.html#echarts.init
197
  , onInit    :: Maybe (EChartsInstance -> Effect Unit)
198
  }
199

200
tooltipTriggerAxis :: Tooltip
201
tooltipTriggerAxis = mkTooltip { trigger: "axis"}
202

203
opts :: Options -> Option
204 205 206 207 208
opts (Options { mainTitle
              , subTitle
              , xAxis
              , yAxis
              , series
209
              , tooltip
210 211
              , addZoom
              }) =
212
  { title: title mainTitle subTitle
213
  , legend
214
  , tooltip
215 216 217 218
  , grid: {containLabel: true}
  , series
  , xAxis
  , yAxis
219
  , dataZoom: if addZoom then [zoom Slider, zoom Inside] else []
220
  , children : unsafeCoerce [] -- TODO
221
  , toolbox: mkToolBox
222 223 224 225
  }

data Zoom = Slider | Inside

226
instance Show Zoom where
227 228 229 230 231 232 233 234 235 236 237 238 239
  show Slider = "slider"
  show Inside = "inside"

zoom :: Zoom -> DataZoom
zoom z = {
  "type": show z
  ,xAxisIndex: 0
  ,filterMode: "empty"
  ,start: 0
  ,end: 100
  }


240
seriesPie :: Series
241 242 243 244 245 246 247 248
seriesPie = seriesPieD1
  { name: "Pie name" }
  (dataSerie <$> [ {name: "t1", value: 50.0}
                 , {name: "t2", value: 45.0}
                 , {name: "t3", value: 65.0}
                 , {name: "t4", value: 15.0}
                 , {name: "t5", value: 23.0}
                 ])
249 250 251 252


textStyle2 :: TextStyle
textStyle2 =
253
  { color: black
254
  , fontStyle: chartFontStyle Italic
255 256 257 258 259 260 261 262 263 264 265 266 267 268
  , fontWeight: chartFontWeight normal
  , fontFamily: "sans-serif"
  , fontSize: 11
  , align: relativePosition $ Relative RightPos
  , verticalAlign: relativePosition $ Relative Bottom
  , lineHeight: percentPosition 0.0
  , width: percentPosition 100.0
  , height: percentPosition 100.0
  , textBorderColor: black
  , textBorderWidth: 0.0
  , textShadowColor: black
  , textShadowBlur: black
  , textShadowOffsetX: 0.0
  , textShadowOffsetY: 0.0
269 270 271 272
  }

textStyle' :: TextStyle
textStyle' =
273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288
  { color: black
  , fontStyle: chartFontStyle normal
  , fontWeight: chartFontWeight normal
  , fontFamily: "sans-serif"
  , fontSize: 15
  , align: relativePosition $ Relative LeftPos
  , verticalAlign: relativePosition $ Relative Top
  , lineHeight: percentPosition 0.0
  , width: percentPosition 100.0
  , height: percentPosition 100.0
  , textBorderColor: black
  , textBorderWidth: 1.0
  , textShadowColor: black
  , textShadowBlur: black
  , textShadowOffsetX: 0.0
  , textShadowOffsetY: 0.0
289 290 291 292
  }

textStyle :: TextStyle
textStyle =
293 294 295 296
  { color: black
  , fontStyle: chartFontStyle normal
  , fontWeight: chartFontWeight normal
  , fontFamily: "sans-serif"
297
  , fontSize: 10
298 299
  , align: relativePosition $ Relative LeftPos
  , verticalAlign: relativePosition $ Relative Top
300
  , lineHeight: percentPosition 10.0
301 302 303 304 305 306 307 308
  , width: percentPosition 100.0
  , height: percentPosition 100.0
  , textBorderColor: black
  , textBorderWidth: 1.0
  , textShadowColor: black
  , textShadowBlur: black
  , textShadowOffsetX: 0.0
  , textShadowOffsetY: 0.0
309
  }