palantir / plottable

:bar_chart: A library of modular chart components built on D3
http://plottablejs.org/
MIT License
2.98k stars 221 forks source link

[Discussion] Interaction API #1095

Closed jtlan closed 10 years ago

jtlan commented 10 years ago

Associated discussion thread for Interaction API Proposal.

bluong commented 10 years ago

So to start this discussion. One thing that I think was 75% resolved is why are Interactions objects instead of inherently attached? Is it strictly so we have ideas of reusability and encapsulation?

teamdandelion commented 10 years ago

The API proposal seems very hand-wavey and incomplete. Could you please flesh it out some more.

jtlan commented 10 years ago

@danmane I'll definitely fill those in. I hadn't finished fleshing out that page yet.

jtlan commented 10 years ago

I proposed this interface on the wiki page:

export interface Hoverable {
  getHovered: (p: Point) => HoverData;
}

export interface HoverData {
  tooltipTarget: D3.Selection;
  data: any;
}

but I actually don't feel too good about the signature there; the return type is pretty clunky. Anyone have ideas on how to generalize the signature?

bluong commented 10 years ago

@jtlan In regards to your sample use case, could the selection be null? Is there a way to enforce that the function is never called when it is null?

jtlan commented 10 years ago

The selection could be null if there's nothing under the cursor. The issue here is that I'm trying to roll up complicated callback construction so the user doesn't have to build one. Maybe the correct approach is to create methods that generate callbacks, rather than trying to build them in. Currently the user has to do something like this:

var hoverInteraction = new Plottable.Interaction.Hover();
var hoverCallback = function(point) {
  var bar = barPlot.selectBar(point.x, point.y, null);
  if (bar != null) {
    // logic to draw tooltip, etc
  }
};
hoverInteraction.onHover(hoverCallback);
barPlot.registerInteraction(hoverInteraction);
bluong commented 10 years ago

So I guess I'm still confused on why Interactions are objects instead of strict interfaces. When I create an interaction, for example, I almost always expect it to be coupled with some component/element from the getgo (Examples of this workflow include jQuery's click, hover, etc.). I remember @danmane saying that one advantage is that there will be lack of duplication if we use objects, but I am currently failing to see this advantage in the current proposed architecture.

bluong commented 10 years ago

Also, I think I'm misunderstanding a possible workflow that's listed in the wiki

class LegendHoverInteraction extends HoverInteraction {
  onHover() { doHoverStuff }
}

class Legend implements Hoverable {
  getHovered() { doHoverStuff }
}

What's the difference between the two? Does attaching the LegendHoverInteraction to Legend just enable getHovered to activate? If so, LegendHoverInteraction seems to be a simple switch...?

jtlan commented 10 years ago

Interactivity is designed as Interaction objects rather than interfaces in order to make the behavior more reusable, and to allow novel combinations of interactions in a way that we didn't necessarily anticipate when writing the library. We could build interactions as mixins, so you get something like this:

class Dragabble { // Implementation }
class Hoverable { // Implementation }
class Zoomable { // Implementation }

public class Scatter extends Plot implements Draggable, Hoverable, Zoomable {
  ...
}

But what happens when someone wants a scatterplot to respond to a click? Or a keypress? Do they have to modify the library?

public class Scatter extends Plot implements Draggable, Hoverable, Zoomable, Clickable, KeyPressAble {
  ...
}

Essentially this approach requires that we exhaustively list the available choices for interactivity, which constrains the user's flexibility.

jtlan commented 10 years ago

I still don't understand your comment about LegendHoverInteraction extends HoverInteraction. We would either have LegendHoverInteraction or have Legend implement Hoverable and supply its own custom logic, not both.

bluong commented 10 years ago

So after thinking about this some more and looking at the wiki after it's updated, I see the DragBoxHandler interface. From a brief implementation perspective, how are the arguments being piped through to the callback? I remember on an earlier wiki that there was a getHovered function attached to components that are Hoverable. Is that where the information is gathered?

bluong commented 10 years ago

Implementation nitpick - Should we put data at the forefront to highlight that the data should be the main variable to interface for whatever handler?

bluong commented 10 years ago

I think it'd be great if we can find a way to avoid custom handlers. This means we have to discuss / figure out 2 things. Narrow down the scope of a handler / generalize the handler across components that will react from the handler.

Let's use tooltips as an example. I can imagine tooltips being implemented as... a mouseInteraction where if you mouseOver a location then a tooltip will show with relevant data and the tooltip will fade on mouseOut.

Now, what does this require? Ignoring the Interaction aspect (I can imagine a mouseInteraction which is essentially a housing for handlers where relevant data is fed to it), what should the behavior be? In the most general case, it seems we should create some tooltip element based on input data and mouse location. Based on the previous sentence, we won't need a PiePlotTooltipHandler or anything such. A single TooltipHandler should be able to do the work.

In other words, we shouldn't need to create a handler for each component to cover 1 task across all components. In the structure I'm thinking, every class might need to implement a function to provide the correct information, but that should be all

jtlan commented 10 years ago

Discussion concluded. I'll update the wiki.