RhoInc / Webcharts

Reusable, flexible, interactive charts with JavaScript
MIT License
34 stars 6 forks source link

Refactor `chart.marks` #176

Closed jwildfire closed 6 years ago

jwildfire commented 6 years ago

It's currently a pain to select marks for customization once they are rendered. There's no reason for this to be the case. The following changes should help:

  1. Allow custom IDs for mark groups: Add a new optional config.marks[].id property. This will be added as a class to the marks "supergroup" and can be used for filtering the marks array. If user doesn't specify an the index of the mark group is used ("markgroup1", "markgroup2" ,etc.)
  2. Link config.marks to chart.marks: Update the chart.marks spec so that each chart.mark object (ie each item in the chart.marks[] array) includes all properties from the corresponding config.marks[] object, as described here, should be included. The wiki implies these are already included, but as of 1.9.0 they are not. Note that chart.marks[].data is currently the only property for the mark objects, and will remain unchanged (even though it's kind of a hot mess).
  3. Create a supergroup property for each mark: This links the d3 selection for the "supergroup" containing all marks. Simplifies the code from const pointGroup = chart.svg.select("g.point-supergroup") to const pointGroup = chart.marks[0].supergroup.
  4. Create an groups property for each mark: This links the d3 selection for all of the groups containing individual marks. Simplifies the code from const points = chart.svg.select("g.point-supergroup").selectAll("g.points") ) to const myPoints = chart.marks[0].wrap or better yet chart.marks.filter().pop().items()
  5. Create mark-level properties when possible: selects circle for mark.type="points", text for mark.type="text" and paths for mark.type="line". No support for bars at this level yet since nesting/grouping etc makes it messy, but could tackle later.
  6. Create a new marks.supergroups property: Link a d3 selection of all the mark supergroups to the chart.mark object itself. This should allow for much easier access to the d3.selection for the mark data.

All told these changes should greatly simplify mark customization in callbacks. For example:

//Fill all circles in the chart above a given threshold
var allCircles = chart.marks.wrap.filter(d=>d.type=='points').selectAll("g.point").select("circle")
allCircles.attr("fill", d=>d.x > config.threshold ? "black", null)

//do stuff when the sites marks are clicked
chart.marks.find(d=>d.id="sites").items.on("click, ...)