Skip to content

Instantly share code, notes, and snippets.

@simerplaha
Last active May 16, 2018 13:40
Show Gist options
  • Save simerplaha/7483a4efe0599b239b31 to your computer and use it in GitHub Desktop.
Save simerplaha/7483a4efe0599b239b31 to your computer and use it in GitHub Desktop.
A simple Scala-js C3.js Charts facade ... Something I wrote quickly to integrate C3.js charts. Hopefully it will be good enough for anyone to get started with C3.js charts in Scala.js. I will try to make the parameters more type safe objects soon and implement the APIs properly.
import scala.scalajs.js
import scala.scalajs.js.JSConverters._
import scala.scalajs.js.annotation.JSName
trait C3ChartObject extends js.Object {
def load(data: C3JSChartDataset): js.Dynamic = js.native
def unload() = js.native
}
@JSName("c3")
object C3Chart extends js.Object {
def generate(data: C3JSChartData): C3ChartObject = js.native
}
trait C3JSChartDataset extends js.Object {
}
object C3JSChartDataset {
def apply(`type`: js.UndefOr[String] = js.undefined,
types: Map[String, String] = Map.empty[String, String],
xs: Map[String, String] = Map.empty[String, String],
ys: Map[String, String] = Map.empty[String, String],
xAxisDataId: js.UndefOr[String] = js.undefined,
data: Map[String, js.Array[Any]],
colors: Map[String, String] = Map.empty[String, String],
axes: Map[String, String] = Map.empty[String, String],
groups: List[List[String]] = List.empty[List[String]]) = {
js.Dynamic.literal(
x = xAxisDataId,
xs = xs.toJSDictionary,
ys = ys.toJSDictionary,
columns = data.values.toJSArray,
groups = groups.map(_.toJSArray).toJSArray,
`type` = `type`,
types = types.toJSDictionary,
keys = js.Dynamic.literal(
value = data.keys.toJSArray
),
colors = colors.toJSDictionary,
axes = axes.toJSDictionary
).asInstanceOf[C3JSChartDataset]
}
}
trait C3JSChartData extends js.Object {
def bindto: String = js.native
def data: C3JSChartDataset = js.native
}
trait Region extends js.Object {
def start: Long = js.native
def end: Long = js.native
def `class`: js.UndefOr[String] = js.native
def style: js.UndefOr[String] = js.native
}
object Region {
def apply(start: Long,
end: Long,
`class`: js.UndefOr[String] = js.undefined,
style: js.UndefOr[String] = js.undefined) = js.Dynamic.literal(
start = start,
end = end,
`class` = `class`,
style = style
).asInstanceOf[Region]
}
object C3JSChartData {
def apply(bindTo: String = "chart",
`size.height`: js.UndefOr[Int] = js.undefined,
`size.width`: js.UndefOr[Int] = js.undefined,
`zoom.enabled`: js.UndefOr[Boolean] = js.undefined,
`zoom.rescale`: js.UndefOr[Boolean] = js.undefined,
`grid.y.show`: js.UndefOr[Boolean] = js.undefined,
xAxisType: js.UndefOr[String] = js.undefined,
xTickFormat: js.UndefOr[(Any) => String] = js.undefined,
yTickFormat: js.UndefOr[(Any) => String] = js.undefined,
`tooltip.format.title`: js.UndefOr[(Double) => String] = js.undefined,
`tooltip.format.value`: js.UndefOr[(Double, js.UndefOr[String], js.UndefOr[String]) => String] = js.undefined,
xAxisLabel: js.UndefOr[String] = js.undefined,
yAxisLabel: js.UndefOr[String] = js.undefined,
y2AxisLabel: js.UndefOr[String] = js.undefined,
fitX: js.UndefOr[Boolean] = js.undefined,
rotateX: js.UndefOr[Int] = js.undefined,
multilineX: js.UndefOr[Boolean] = js.undefined,
cullingMaxX: js.UndefOr[Int] = js.undefined,
heightX: js.UndefOr[Int] = js.undefined,
showAdditionalYAxis: js.UndefOr[Boolean] = js.undefined,
rotated: js.UndefOr[Boolean] = js.undefined,
regions: js.UndefOr[js.Array[Region]] = js.undefined,
showPoint: Boolean = true,
data: C3JSChartDataset): C3JSChartData = {
js.Dynamic.literal(
bindto = s"#$bindTo",
data = data,
regions = regions,
grid = js.Dynamic.literal(
y = js.Dynamic.literal(
show = `grid.y.show`
)
),
size = js.Dynamic.literal(
height = `size.height`,
width = `size.width`
),
zoom = js.Dynamic.literal(
enabled = `zoom.enabled`,
rescale = `zoom.rescale`
),
point = js.Dynamic.literal(
show = showPoint
),
tooltip = js.Dynamic.literal(
format = js.Dynamic.literal(
title = `tooltip.format.title`,
value = `tooltip.format.value`
)
),
axis = js.Dynamic.literal(
x = js.Dynamic.literal(
`type` = xAxisType,
label = xAxisLabel,
height = heightX,
tick = js.Dynamic.literal(
format = xTickFormat,
fit = fitX,
rotate = rotateX,
multiline = multilineX,
culling = js.Dynamic.literal(
max = cullingMaxX
)
)
),
y = js.Dynamic.literal(
label = yAxisLabel,
tick = js.Dynamic.literal(
format = yTickFormat
)
),
y2 = js.Dynamic.literal(
label = y2AxisLabel,
show = showAdditionalYAxis
),
rotated = rotated
)
).asInstanceOf[C3JSChartData]
}
}
@ant8e
Copy link

ant8e commented Oct 9, 2015

Thanks for that, it is exactly what i needed.
What is the license for this code ?

@simerplaha
Copy link
Author

Good to hear it's helping someone :)
hmm there is no license. It's just a code snippet and I never really thought about license. But if you use it and improve it by adding more types and more API's please do let me know or update it :)

@Rodrigo-Martinez-Jacobson

Hello, is there any tutorial on how to use this facade?

@simerplaha
Copy link
Author

I don't really have a tutorial. But the C3js documentation is very well written. After you create your C3JSChartData object all you have to do is invoke the generate method on C3Chart object (c3 in Javascript) passing it your data.

C3Chart.generate(yourChartData)

And the above will render/generate the Chart.

Javascript equivalent would be

c3.generate({yourChartData});

you can see one of the C3.js example here to see how they are creating their input objects in JSON. To get you started below is a small sample (untested!). I'm also using scalajs-momentjs here to format this timeseries chart's x axis to D MMM YYYY, HH:mm.

div(id := "myChartId") //Scalatags

val myChartData = C3JSChartData(
      `zoom.enabled` = true,
      `zoom.rescale` = true,
      `size.height` = 370,
      bindTo = "myChartId",
      xAxisType = "timeseries",
      xTickFormat = (value: Any) => Moment(value.toString).format(`D MMM YYYY, HH:mm`),
      xAxisLabel = "Date",
      yAxisLabel = "Value",
      showAdditionalYAxis = false,
      rotateX = -90,
      multilineX = false,
      cullingMaxX = 30,
      heightX = 100,
      data = C3JSChartDataset(
        `type` = "line",
        x = "date",
        data = myData
      )
    )

C3Chart.generate(myChartData)

@Rodrigo-Martinez-Jacobson

I'm having problems with the input data, it doesn't like the array type being Any :

data: Map[String, js.Array[Any]]

But it works if I change it to Double or Int

@Rodrigo-Martinez-Jacobson

Trying with a List[Any] seems to compile, but the chart doesn't show and I get the following error in the javascript console:

Uncaught TypeError: (0 , $g.C3JSChartDataset) is not a function

The list i'm using is this one:
val exampleList: List[Any] = List("angelitos",30,50,60,80)

@prassee
Copy link

prassee commented Mar 2, 2018

data = C3JSChartDataset(
        `type` = "line",
        x = "date",
        data = myData
      )

in C3JSChartDataSet i don't see the variable x using this code throws a error please verify

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment