Rietveld Code Review Tool
Help | Bug tracker | Discussion group | Source code | Sign in
(1044)

Unified Diff: docs/d3-component-framework.rst

Issue 6858085: D3 Components Documentation
Patch Set: D3 Components Documentation Created 11 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Please Sign in to add in-line comments.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « [revision details] ('k') | docs/index.rst » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: docs/d3-component-framework.rst
=== added file 'docs/d3-component-framework.rst'
--- docs/d3-component-framework.rst 1970-01-01 00:00:00 +0000
+++ docs/d3-component-framework.rst 2012-11-29 03:54:35 +0000
@@ -0,0 +1,262 @@
+======================
+D3 Component Framework
+======================
+
+The D3 Component codebase is a small framework with the following goals:
+
+- Support clear separation of concerns among various sections of application
+ logic.
+- Support incremental updates of the D3 scene.
+- Allow simple configuration of features to aid in the reusability of D3
+ application logic.
+
+It accomplishes this through a number of declarative tools and suggested
+patterns around application development. The document below attempts to
+explain this usage as it exists today.
+
+The framework only models two high level objects, Component and Module. A
+Component is a top level container around Module objects. The Component
+provides the implementation of the declarative behavior provided by the
+framework. The Module(s) implement the logical sections of the application. In
+the YUI world it might be common to expect each Module to be the application's
+rendering and interactive behavior around a single YUI App.Model or
+App.ModelList.
+
+Module Writers Guide
+====================
+
+Using the component framework means taking advantage of the tools offered
+through its module system. Components manage one or more modules, each of which
+models one concern of the resultant scene.
+
+Modules declare events using structures dictated by the framework. Event
+bindings allow the module to respond to both changes in its underlying data and
+to changes in application state (through things like user interaction). It is
+through these bound events that most rendering and interaction with the scene
+are performed. Render becomes a rarely invoked complete redraw of the scene
+while the modules themselves handle incremental updates via their event
+handlers.
+
+Component API
+=============
+
+Components support few actions (as most of the non-declarative work happens in
+Modules). Components are containers for modules and have support functions for
+adding and removing modules from their management. In addition, when modules
+are added options can be passed that will be made available in the Module's
+run time. Modules added via the ``addModule`` method automatically have a
+reference to the component via an YUI Attribute named ``component``, and any
+options passed to the addModule call result in an Attribute called ``options``,
+which will default to an empty object.
+
+::
+
+ comp = new Component();
+ comp.addModule(MyModule, {foo: bar})
+
+This example would create a new component and, then using the MyModule
+constructor, create an instance of this module (or use an instance if directly
+passed) and set the ``component`` and ``options`` Attributes. In this example
+the Module would have its ``foo`` option set to ``bar`` such that::
+
+ modInstance.get('options.foo') === 'bar'
+
+where modInstance is the instance created by the above example's addModule call.
+
+Components then support rendering, which draws the scene described by any data
+the modules reference. This is done in a number of phases. The first (and often
+only) time ``render`` is called, a special ``renderOnce`` is called. This
+allows the component to do any necessary setup work. Typically this will be to
+create some svg element and retain a reference to it. In the context of
+renderOnce, ``render`` will call ``update`` on each of its modules in the order
+they were added. The ``update`` method is used to recalculate any intermediate
+state associated with rendering the various data objects. This includes things
+like position information. Once ``update`` has been called each module's
+``render`` method is invoked. This performs the actual drawing work. It is at
+this point that any d3js synthetic events are bound to the canvas (see the
+section on events below). This separation of phases exists to make the life of
+the Module writer simpler. They can rely on whatever elements they'll be drawing
+ (adding children to an svg object for example) will already have its container
+ properly available due to the renderOnce setup. They can also be sure that
+ any ``update`` driven intermediate data will be computed and available for
+ use in ``render``. This reduces the need for checks in each module to assert
+ the most basic DOM state.
+
+After the initial render its expected that updates to the scene occur via the
+event handlers in the various modules. Render will not usually need to be called
+more than once unless the entire Component rendering is removed from the DOM and
+then later re-attached.
+
+The most important aspect of addModule (and its inverse removeModule) is that
+they properly support adding and removing event listeners. When a component's
+addModule method is triggered it will bind all the declarative events of the
+module, and when removeModule is called it will properly clean up event
+subscriptions. Properly defining and using events is the core of the component
+system and this is described in its own section.
+
+As a final step, if the module has a ``componentBound`` callback it will be
+invoked after successfully binding the module. This gives the module a
+chance to initialize any data that depends on component state (which can
+be obtained through ``.get('component')``).
+
+Events
+======
+
+The heart of the component system events can be defined in a number of ways and
+understanding how to take advantage of the binding features will greatly aid in
+producing a system which allows for clean, clear, well separated concerns and
+which in turn supports incremental rendering and data updates.
+
+There are three categories of events supported by the component framework. They
+each have their purpose and share some common syntax around expression in the
+module declaration, but a module writer must understand them all to properly use
+the framework.
+
+When modules are added three sets of declarative events are bound. This is done
+by including in the module an events object with the following (each optional)
+sections::
+
+ events = {scene: {},
+ d3: {}
+ yui: {}
+ };
+
+Most commonly there are ``scene`` events. Scene events describe YUI style event
+delegation. This has advantages in that it scales well and these events can be
+bound once to the top level container object of a component, and will work for
+a DOM element matching its selector. Scene events are defined in one of two
+ways: the first is a shorthand, the later is the more complete definition
+(you'll see this in the other events types as well).
+
+::
+
+ scene: { selector: 'callback name on module'}
+
+also supported is::
+
+ scene: {selector: function() {...}}
+
+Though the string name of a callback is preferred, as this makes the whole
+set of events more easily readable. The final form is::
+
+ scene: {selector: {callback: 'callbackName'}}
+
+This expanded format is common to the other types of event declarations as
+well as supporting options available to the other types of bindings.
+
+Regardless of form selector is a CSS selector, typically either a ``.class`` or
+an ``#id`` though pseudo-selectors work as well. With scene events these
+selectors are relative to whatever container was established on initialization
+of the Component. A concrete example might be::
+
+ scene: {'.person': {click: 'personClick'}}
+
+Which says that whenever an object in the scene with a ``person`` class is
+clicked, invoke the ``personClick`` handler. Handlers all have a common signature.
+To understand the calling convention you must understand a bit about how D3
+data bindings work. If you're not familiar with that, please read the D3
+documents related to data binding. The short version is that each DOM element
+can have data associated with it through D3's sophisticated data binding model.
+In the YUI world it might be common that rendered DOM elements have D3 bound
+data coming from a YUI App.Model. Knowing this we can understand the calling
+convention::
+
+ callback(D3Data, component)
+ Where 'this' is the DOM element that triggered the selection
+ Any return is ignored.
+
+In the near future scene events will support an additional context attribute in
+their handler definition which can either be ``component`` or ``module`` and will
+default to module.
+
+.. note::
+
+ At the time of this writing this is currently component and doesn't support
+ context selection. This is addressed in a branch and when landed this note
+ can be removed. It's worth noting now as the default will change.
+
+The second type of event are D3 specific bindings. While declared in a style
+similar to scene events, D3 events are bound after the modules render method is
+triggered, as DOM elements must be present to be bound. There are very few cases
+to prefer this style of event binding over normal scene events; however, there
+are legitimate uses. If the event is a D3 synthetic event such as zoom or drag,
+using D3 event bindings make sense as these cannot be delegated to using scene
+events. The second case we are aware of at the time of this writing is that
+certain mouse events are dealt with more easily using D3 events, as D3 uses a
+well documented system of x, y position coordinates which the mouse events map
+cleanly. This is a possible area for future expansion both in terms of cleaner
+mouse handling and creating a possible mapping of D3 synthetics to YUI custom
+events. An example of D3 events follows::
+
+ d3: {dragstart: 'beginDrag',
+ drag: 'redrawConnectors',
+ dragend: 'savePosition'}
+
+The calling convention is as above::
+
+ callback(D3Data, component)
+ 'this' is the DOMElement triggering the event.
+ Return value is ignored.
+
+The final type of event is called ``yui`` events. This classification doesn't
+depend on DOM selection or delegation, and is designed to provide simple
+handling; its use case is YUI custom events. A common pattern for
+usage might be to emit events of interest (or possible interest) from one
+module and listen for those events in another. By subscribing to custom events
+across modules, it's reasonably easy to extend functionality with only a loose
+coupling of the modules themselves (through event names only as an example).
+
+YUI events are defined similarly to the others but differ in some key ways.
+First, they don't depend on a DOM selector, they depend on a YUI styled event
+name (prefixed or otherwise). Secondly, they support a traditional YUI notion
+of event phases: ``before``, ``on`` and ``after``. For additional details on how those
+work, refer to the YUI event docs.
+
+::
+
+ yui: {'cancelAction': {callback: 'closeMenu',
+ phase: 'before',
+ context: 'module'
+ }
+ }
+
+In this example another module might fire a ``cancelAction`` event; our module
+wants to respond to this by closing its menu before the triggering event is
+handled, and the context (this) of the callback should be this module.
+
+Context can either be ``component`` or ``module``, with module being the default
+``this`` for handlers. Phase can be ``before``, ``on``, or ``after``, with ``on`` being
+the default.
+
+Complete Example
+================
+
+Here is a complete example of a module, with some description. The tests for
+this framework also can be used to learn about the capabilities and expected
+usage of the system.
+
+::
+
+ TestModule = Y.Base.create('TestModule', Module, [], {
+ events: {
+ scene: { '.thing': {click: 'decorateThing'}},
+ d3: {drag: 'dragObject'},
+ yui: { cancel: 'cancelHandler'}
+ },
+
+ decorateThing: function(data, context) {
+ // this is a DOM .thing element that was clicked
+ // data is D3 bound data, context will be the module.
+ },
+
+ dragObject: function(data, context) {
+ // this is a DOM element that had the D3.behavior.drag applied
+ // and was then dragged with a mouse event.
+ // data is D3 bound data, context will be the module.
+ },
+
+ cancelHandler: function(evt) {
+ // this is the module
+ // evt is the YUI event object
+ }
+ });
« no previous file with comments | « [revision details] ('k') | docs/index.rst » ('j') | no next file with comments »

Powered by Google App Engine
RSS Feeds Recent Issues | This issue
This is Rietveld f62528b