charter-0.1.1.0: src/Charts/Internal/Chart.hs
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE PolyKinds #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE ScopedTypeVariables #-}
module Charts.Internal.Chart where
import qualified Data.Text as T
import Data.Aeson as A
-- | Valid Column types. Each "data" column also accepts a column header.
-- "Role" columns do not.
--
-- See https://developers.google.com/chart/interactive/docs/roles
data Column =
NumberColumn T.Text
| StringColumn T.Text
| BoolColumn T.Text
| DateColumn T.Text
| DateTimeColumn T.Text
| TimeOfDayColumn T.Text
| AnnotationColumn
| AnnotationTextColumn
| CertaintyColumn
| EmphasisColumn
| IntervalColumn
| ScopeColumn
| StyleColumn
| TooltipColumn
| DomainColumn
-- ^ This is typically inferred and explicitly
| DataColumn
-- ^ This is typically inferred and explicitly
deriving (Show, Eq)
-- Types from https://developers.google.com/chart/interactive/docs/reference#DataTable_addColumn
instance ToJSON Column where
toJSON col =
case col of
StringColumn a -> object [ "label" .= a, "type" ~= "string"]
NumberColumn a -> object [ "label" .= a, "type" ~= "number"]
BoolColumn a -> object [ "label" .= a, "type" ~= "boolean"]
DateColumn a -> object [ "label" .= a, "type" ~= "date"]
DateTimeColumn a -> object [ "label" .= a, "type" ~= "datetime"]
TimeOfDayColumn a -> object [ "label" .= a, "type" ~= "timeofday"]
AnnotationColumn -> object ["role" ~= "annotation"]
AnnotationTextColumn -> object ["role" ~= "annotationText"]
CertaintyColumn -> object ["role" ~= "certainty"]
EmphasisColumn -> object ["role" ~= "emphasis"]
IntervalColumn -> object ["role" ~= "interval"]
ScopeColumn -> object ["role" ~= "scope"]
StyleColumn -> object ["role" ~= "style"]
TooltipColumn -> object ["role" ~= "tooltip"]
DomainColumn -> object ["role" ~= "domain"]
DataColumn -> object ["role" ~= "data"]
where
(~=):: T.Text -> T.Text -> (T.Text, Value)
(~=) = (.=)
-- | Supported chart types
-- See https://developers.google.com/chart/interactive/docs/gallery
data ChartStyle = LineChart
| Histogram
| BarChart
| ColumnChart
| ScatterChart
| AreaChart
| PieChart
| BubbleChart
| SteppedAreaChart
| CandlestickChart
deriving (Show, Eq, Ord)
instance ToJSON ChartStyle where
toJSON = \case
LineChart -> "line"
Histogram -> "histogram"
BarChart -> "bar"
ColumnChart -> "column"
ScatterChart -> "scatter"
AreaChart -> "area"
PieChart -> "pie"
BubbleChart -> "bubble"
SteppedAreaChart -> "steppedarea"
CandlestickChart -> "candlestick"
-- | I plan to make this typesafe for each partiular chart type
-- but that's a LOT of work, so for now it's just free-form, if you'd actually
-- use this, please make an issue on Github so I know folks need this behaviour :)
--
-- Find your chart in the chart gallery to see which options it will accept.
--
-- https://developers.google.com/chart/interactive/docs/gallery
data ChartOptions = ChartOptions Value
instance ToJSON ChartOptions where
toJSON (ChartOptions v) = v
-- | Empty chart options
defaultChartOptions :: ChartOptions
defaultChartOptions = ChartOptions (object [])
-- | The primary chart type. Build using 'buildChart'.
data Chart =
Chart { options :: ChartOptions
, style :: ChartStyle
, columns :: [Column]
, dataTable :: [[Value]]
, dynamic :: Bool
}
instance ToJSON Chart where
toJSON (Chart{..}) =
object [ "rows" .= dataTable
, "options" .= options
, "columns" .= columns
, "style" .= style
, "dynamic" .= dynamic
]
-- | Construct a chart.
--
-- e.g.
--
-- @
-- buildChart defaultChartOptions BarChart
-- [StringColumn "Year", NumberColumn "Population"]
-- [ [ String "2004", Number 1000 ]
-- , [ String "2005", Number 1170 ]
-- , [ String "2006", Number 660 ]
-- , [ String "2007", Number 1030 ]
-- ]
-- @
buildChart :: ChartOptions -> ChartStyle -> [Column] -> [[Value]] -> Chart
buildChart opts style columns vals = Chart opts style columns vals True