Halogen ECharts Simple
On Sat, 10 Dec 2022, by @lucasdicioccio, 529 words, 8 code snippets, 6 links, 0images.
This page is a demonstration of a ECharts-for-Halogen library I published. The library spawned out of this very-blog (or rather, from Kitchen-Sink) to display the page graph on the home page.
Installation
Assuming you use npm
and spago
.
npm i echarts
spago install halogen-echarts-simple
Usage
The simple
in the title means that we provide a very simple layer. That is,
you have to fill in the blanks.
What this library offers is a function to return Halogen components provided that you teach it:
- (a) what is the type of the
options
object passed to EChart - (b) what sort of click-callback objects you care to capture
The type Options o = {|o}
definition embodies the (a)
above.
The type Output i = {|i}
definition embodies the (b)
above.
You thus need some boilerplate work to type and translate the various ECharts datatypes (with its flurry of possible branches). That is, rather than trying to build a all-you-can-eat-buffet type that would match every ‘options’, we prefer to restrict ourselves to simple situations where a given graph has exactly one type. Besides the boilerplate, there probably are limitations (I have not tried the most advanced charts options involving JavaScript functions yet). For situations where the ECharts options merely are uniform data, the boilerplate work should be pretty straightforward.
example: simple line chart
Here we start translating the line-simple example.
Purescript definitions
We need two things to make the example work: a Slot for your Halogen component, and a type to teach PureScript what is the shape of the options
object for ECharts.
type Slots =
echarts :: forall query output. H.Slot query output Unit
(
)= Proxy :: Proxy "echarts"
_echarts
type SimpleExampleOptions =
xAxis :: { type :: String, data :: Array String }
{ yAxis :: { type :: String }
, series :: Array { data :: Array Int, type :: String }
, }
single line
Then we can call ECharts.component
to turn options into an Halogen Component, which you include in a Slot thanks to the HH.slot_
function.
Thus, the rendering function for your enclosing component needs to call ECharts.components
.
render0 =
let
obj :: SimpleExampleOptions
obj =
{ xAxis: {type: "category", data: ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"]}
, yAxis: {type: "value"}
, series: [{type: "line", data: [150, 230, 224, 218, 135, 147, 260]}]
}
in
HH.div_
[ HH.text "echarts simple-line example"
, HH.slot_ _echarts unit ECharts.component {options: obj, modified:false}
]
two lines
=
render1 let
obj :: SimpleExampleOptions
=
obj : {type: "category", data: ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"]}
{ xAxis: {type: "value"}
, yAxis:
, seriestype: "line", data: [150, 230, 224, 218, 135, 147, 260]}
[{type: "line", data: [250, 130, 254, 318, 137, 247, 160]}
,{
]
}in
HH.div_"echarts simple-line example with two data-lines"
[ HH.text : obj, modified:false}
, HH.slot_ _echarts unit ECharts.component {options ]
interactive
We need to define some action for our enclosing component. The component has an event for a numeric HTML input form.
data DemoAction
= SetChart2Offset String
Now we can update the ECharts options at each re-render.
=
render2 offset let
= [150, 230, 224, 218, 135, 147, 260]
ys obj :: SimpleExampleOptions
=
obj : {type: "category", data: ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"]}
{ xAxis: {type: "value"}
, yAxis:
, seriestype: "line", data: ys }
[{type: "line", data: map (\v -> v + offset) ys}
,{
]
}in
HH.div_"echarts simple-line example with an offset" ]
[ HH.p_ [ HH.text "we update the offset with the value in the input below:" ]
, HH.p_ [ HH.text
, HH.inputHP.InputNumber
[ HP.type_ $ show offset
, HP.value SetChart2Offset
, HE.onValueInput
]: obj, modified:true}
, HH.slot_ _echarts unit ECharts.component {options
]
= case _ of
handleAction SetChart2Offset str -> do
case Int.fromString str of
Nothing -> pure unit
Just n -> H.modify_ _ { chart2Offset = n }
catching clicks
Say we want to add handlers to catch clicks on the chart. ECharts has some provisions for such events and with a bit of boilerplate we can recover the event data.
Note that, much like for Options types we pass to ECharts, we need to type the sort event data we expect to receive from the JavaScript callback, and prepare an action for the enclosing component.
type SimpleExampleEvent =
name :: String
( seriesIndex :: Int
, dataIndex :: Int
,
)
data DemoAction
= SetChart2Offset String
| Chart3Event (Record SimpleExampleEvent)
Now our component rendering and handling actions can be as follows:
=
render3 item let
obj :: SimpleExampleOptions
=
obj : {type: "category", data: ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"]}
{ xAxis: {type: "value"}
, yAxis:
, seriestype: "line", data: [150, 230, 224, 218, 135, 147, 260]}
[{type: "line", data: [250, 130, 254, 318, 137, 247, 160]}
,{
]
}= case item of
renderItem Nothing -> HH.p_ [ HH.text "nothing clicked" ]
Just r -> HH.div_
"some click!" ]
[ HH.h6_ [ HH.text .name ]
, HH.p_ [ HH.text r
, HH.p_
[ HH.text $ fold
"seriesIndex/dataIndex: "
[ show r.seriesIndex
, " / "
, show r.dataIndex
,
]
]
]in
HH.div_"echarts simple-line example with clickable callback" ]
[ HH.p_ [ HH.text
, renderItem: obj, modified:false} Chart3Event
, HH.slot _echarts unit ECharts.component {options
]
= case _ of
handleAction Chart3Event ev -> do
= Just ev }
H.modify_ _ { chart3ClickEvent
the modified :: Bool
Input parameter
From the above example you may have noticed that the Input object requires
extra information. In particular, the modified
boolean allows you to tune whether you want
to re-render the graph each time the component is re-rendered or only using the
explicit Query-ing mechanism that Halogen offers. Typically you will hardcode
the modified
value depending on how often component re-renders are in your
application.
full source-code
You’ll find the full source-code of the generated JavaScript in a separate file.
The source-code uses HumDrum to be able to insert a same JS file one time per different example.
want more examples? contact me