{"id":13481580,"url":"https://github.com/curran/d3-component","last_synced_at":"2025-06-20T16:35:21.744Z","repository":{"id":66869817,"uuid":"41185841","full_name":"curran/d3-component","owner":"curran","description":"A lightweight component abstraction for D3.js.","archived":false,"fork":false,"pushed_at":"2017-12-18T23:21:28.000Z","size":251,"stargazers_count":109,"open_issues_count":6,"forks_count":10,"subscribers_count":9,"default_branch":"master","last_synced_at":"2025-04-12T18:14:16.738Z","etag":null,"topics":["component","data-visualization","html5","interaction","interactive-visualizations","user-interface","web-application"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"bsd-3-clause","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/curran.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null}},"created_at":"2015-08-22T02:06:59.000Z","updated_at":"2024-11-06T04:13:11.000Z","dependencies_parsed_at":null,"dependency_job_id":"09800ca6-b18d-418d-b0c4-01d145221ca3","html_url":"https://github.com/curran/d3-component","commit_stats":{"total_commits":354,"total_committers":4,"mean_commits":88.5,"dds":0.04802259887005644,"last_synced_commit":"bf620b4f6e5cd4684a1cd81a95011b490180ba50"},"previous_names":[],"tags_count":23,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/curran%2Fd3-component","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/curran%2Fd3-component/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/curran%2Fd3-component/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/curran%2Fd3-component/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/curran","download_url":"https://codeload.github.com/curran/d3-component/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248610336,"owners_count":21132922,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["component","data-visualization","html5","interaction","interactive-visualizations","user-interface","web-application"],"created_at":"2024-07-31T17:00:53.037Z","updated_at":"2025-04-12T18:14:48.488Z","avatar_url":"https://github.com/curran.png","language":"JavaScript","readme":"# d3-component\n\nA lightweight component abstraction for [D3.js](d3js.org).\n\n**Features:**\n\n * Encapsulates the [General Update Pattern](https://github.com/d3/d3-selection#selection_merge).\n * Composable (even [recursive](https://bl.ocks.org/curran/2aafab81bb2029e1f4f24d258b790ce4)!) stateless functional components.\n * Reliable `destroy` hook for cleaning things up.\n * Works great with [Redux](http://redux.js.org/).\n\n**Examples:**\n\u003ctable\u003e\n  \u003ctr\u003e\n    \u003c!-- Posts --\u003e\n    \u003ctd\u003e\n      \u003cdiv\u003e\n        \u003ca href=\"http://bl.ocks.org/curran/fc8f6989901628e2e79d6374849453ed\"\u003ePosts\u003c/a\u003e | \u003ca href=\"https://bl.ocks.org/micahstubbs/23a43e0236d235fe52855dc6aecd74a0\"\u003eES6 version\u003c/a\u003e\n      \u003c/div\u003e\n      \u003ca href=\"http://bl.ocks.org/curran/fc8f6989901628e2e79d6374849453ed\"\u003e\n        \u003cimg width=\"230\" height=\"120\" src=\"http://bl.ocks.org/curran/raw/fc8f6989901628e2e79d6374849453ed/thumbnail.png\"\u003e\n      \u003c/a\u003e\n    \u003c/td\u003e\n    \u003c!-- Spinner --\u003e\n    \u003ctd\u003e\n        \u003cdiv\u003e\n          \u003ca href=\"http://bl.ocks.org/curran/685fa8300650c4324d571c6b0ecc55de\"\u003eSpinner\u003c/a\u003e | \u003ca href=\"https://bl.ocks.org/micahstubbs/0e2b63921f5642f0f65f51e27cccd02f\"\u003eES6 version\u003c/a\u003e\n        \u003c/div\u003e\n        \u003ca href=\"http://bl.ocks.org/curran/685fa8300650c4324d571c6b0ecc55de\"\u003e\n          \u003cimg width=\"230\" height=\"120\" src=\"http://bl.ocks.org/curran/raw/685fa8300650c4324d571c6b0ecc55de/thumbnail.png\"\u003e\n        \u003c/a\u003e\n    \u003c/td\u003e\n    \u003c!-- Stopwatch (Redux) --\u003e\n    \u003ctd\u003e\n        \u003cdiv\u003e\n          \u003ca href=\"http://bl.ocks.org/curran/593ffae30c42789a9af36f08c983867e\"\u003eStopwatch (Redux)\u003c/a\u003e | \u003ca href=\"https://bl.ocks.org/micahstubbs/8e94f74c7d0a5dec710330574781fda0\"\u003eES6 version\u003c/a\u003e\n        \u003c/div\u003e\n        \u003ca href=\"http://bl.ocks.org/curran/593ffae30c42789a9af36f08c983867e\"\u003e\n          \u003cimg width=\"230\" height=\"120\" src=\"http://bl.ocks.org/curran/raw/593ffae30c42789a9af36f08c983867e/thumbnail.png\"\u003e\n        \u003c/a\u003e\n    \u003c/td\u003e\n  \u003c/tr\u003e\n  \u003ctr\u003e\n    \u003c!-- Counter (Redux) --\u003e\n    \u003ctd\u003e\n      \u003cdiv\u003e\n        \u003ca href=\"http://bl.ocks.org/curran/acde35df0c7939ff97740b4f9800258e\"\u003eCounter (Redux)\u003c/a\u003e | \u003ca href=\"https://bl.ocks.org/micahstubbs/04df008db0b12474a726a7986d73ad14\"\u003eES6 version\u003c/a\u003e\n      \u003c/div\u003e\n      \u003ca href=\"http://bl.ocks.org/curran/acde35df0c7939ff97740b4f9800258e\"\u003e\n        \u003cimg width=\"230\" height=\"120\" src=\"http://bl.ocks.org/curran/raw/acde35df0c7939ff97740b4f9800258e/thumbnail.png\"\u003e\n      \u003c/a\u003e\n    \u003c/td\u003e\n    \u003c!-- Todos --\u003e\n    \u003ctd\u003e\n      \u003ca href=\"http://bl.ocks.org/curran/d8639546697c7ae3ab46c2544683d53a\"\u003e\n        \u003cdiv\u003eTodos\u003c/div\u003e\n        \u003cimg width=\"230\" height=\"120\" src=\"http://bl.ocks.org/curran/raw/d8639546697c7ae3ab46c2544683d53a/thumbnail.png\"\u003e\n      \u003c/a\u003e\n    \u003c/td\u003e\n    \u003c!-- Scatter Plot with Menus (Redux) --\u003e\n    \u003ctd\u003e\n      \u003cdiv\u003e\n        \u003ca href=\"http://bl.ocks.org/curran/8c131a74b85d0bb0246233de2cff3f52\"\u003eScatter Plot with Menus (Redux)\u003c/a\u003e\n        \u003cbr\u003e\n        \u003ca href=\"http://bl.ocks.org/micahstubbs/9180020ff8265eade016a82ba54db26b\"\u003eES6 version\u003c/a\u003e | \u003ca href=\"http://bl.ocks.org/micahstubbs/b097f9d0999848ee70b74b36ce30cbb8\"\u003eRadius select variant\u003c/a\u003e\n      \u003c/div\u003e\n      \u003ca href=\"http://bl.ocks.org/curran/8c131a74b85d0bb0246233de2cff3f52\"\u003e\n        \u003cimg width=\"230\" height=\"120\" src=\"https://cloud.githubusercontent.com/assets/68416/23792933/ff5f7142-05ae-11e7-97dc-9489438dc3e6.png\"\u003e\n      \u003c/a\u003e\n    \u003c/td\u003e\n  \u003c/tr\u003e\n  \u003ctr\u003e\n    \u003c!-- Clock --\u003e\n    \u003ctd\u003e\n      \u003ca href=\"http://bl.ocks.org/curran/28047fcfeb11cc0c66d10e7f30e1fe22\"\u003e\n        \u003cdiv\u003eClock\u003c/div\u003e\n        \u003cimg width=\"230\" height=\"120\" src=\"http://bl.ocks.org/curran/raw/28047fcfeb11cc0c66d10e7f30e1fe22/thumbnail.png\"\u003e\n      \u003c/a\u003e\n    \u003c/td\u003e\n    \u003c!-- Airport Clocks --\u003e\n    \u003ctd\u003e\n      \u003ca href=\"http://bl.ocks.org/curran/598e90fe0dcab73030dbecc38fb6e725\"\u003e\n        \u003cdiv\u003eAirport Clocks\u003c/div\u003e\n        \u003cimg width=\"230\" height=\"120\" src=\"http://bl.ocks.org/curran/raw/598e90fe0dcab73030dbecc38fb6e725/thumbnail.png\"\u003e\n      \u003c/a\u003e\n    \u003c/td\u003e\n    \u003c!-- example-viewer --\u003e\n    \u003ctd\u003e\n      \u003ca href=\"https://github.com/curran/example-viewer\"\u003e\n        \u003cdiv\u003eexample-viewer (Redux, ES6)\u003c/div\u003e\n        \u003cimg width=\"230\" height=\"120\" src=\"https://cloud.githubusercontent.com/assets/68416/24115062/2e694866-0dc8-11e7-8dee-73bfcb42ff13.png\"\u003e\n      \u003c/a\u003e\n    \u003c/td\u003e\n  \u003c/tr\u003e\n  \u003ctr\u003e\n    \u003c!-- Fractal Pie Chart --\u003e\n    \u003ctd\u003e\n      \u003ca href=\"http://bl.ocks.org/curran/2aafab81bb2029e1f4f24d258b790ce4\"\u003e\n        \u003cdiv\u003eFractal Pie Chart (ES6)\u003c/div\u003e\n        \u003cimg width=\"230\" height=\"120\" src=\"http://bl.ocks.org/curran/raw/2aafab81bb2029e1f4f24d258b790ce4/thumbnail.png\"\u003e\n      \u003c/a\u003e\n    \u003c/td\u003e\n  \u003c/tr\u003e\n\u003c/table\u003e\n\nUsing this component abstraction, you can easily encapsulate data-driven user interface components as conceptual \"boxes-within-boxes\", cleanly isolating concerns for various levels of your DOM tree. This component abstraction is similar in concept and functionality to [React Stateless Functional Components](https://hackernoon.com/react-stateless-functional-components-nine-wins-you-might-have-overlooked-997b0d933dbc#.dc21r5uj4). Everything a component needs to render itself and interact with application state gets passed down through the component tree at render time. Components don't store any local state; this is the main difference between d3-component and the [Towards Reusable Charts](https://bost.ocks.org/mike/chart/) pattern. No special treatment is given to events or event delegation, because the intended use is within a unidirectional data flow architecture like [Redux](http://redux.js.org/).\n\n## Installing\n\nIf you use NPM, `npm install d3-component`. Otherwise, download the [latest release](https://github.com/curran/d3-component/releases/latest). You can also load directly from [unpkg.com](https://unpkg.com) as a [standalone library](https://unpkg.com/d3-component@1). AMD, CommonJS, and vanilla environments are supported. In vanilla, a `d3` global is exported:\n\n```html\n\u003cscript src=\"https://unpkg.com/d3@4\"\u003e\u003c/script\u003e\n\u003cscript src=\"https://unpkg.com/d3-component@3\"\u003e\u003c/script\u003e\n\u003cscript\u003e\n  var myComponent = d3.component(\"div\");\n\u003c/script\u003e\n```\n\n## API Reference\n\n**Note:** There was a recent [major version release](https://github.com/curran/d3-component/releases/tag/v3.0.0), and along with it there were substantial [API Changes](https://github.com/curran/d3-component/issues/70).\n\nIn summary, the API looks like this:\n\n```js\nvar myComponent = d3.component(\"div\", \"some-class\")\n  .create((selection, d, i) =\u003e { ... }) // Invoked for entering component instances.\n  .render((selection, d, i) =\u003e { ... }) // Invoked for entering AND updating component instances.\n  .destroy((selection, d, i) =\u003e { ... }); // Invoked for exiting instances, may return a transition.\n\n// To invoke the component,\nd3.select(\"body\") // create a selection with a single element,\n  .call(myComponent, \"Hello d3-component!\"); // then use selection.call().\n```\n\nTo see the full API in action, check out this [\"Hello d3-component\" example](https://bl.ocks.org/curran/c3d9783e641636479fa8e07a480e7233).\n\n\u003ca href=\"#component\" name=\"component\"\u003e#\u003c/a\u003e \u003cb\u003ecomponent\u003c/b\u003e(\u003ci\u003etagName\u003c/i\u003e[, \u003ci\u003eclassName\u003c/i\u003e]))\n\nCreates a new component generator that manages and renders into DOM elements of the specified *tagName*.\n\nThe optional parameter *className* determines the value of the `class` attribute on the DOM elements managed.\n\n\u003ca href=\"#component_create\" name=\"component_create\" \u003e#\u003c/a\u003e \u003ci\u003ecomponent\u003c/i\u003e.\u003cb\u003ecreate\u003c/b\u003e(\u003ci\u003efunction(selection, d, i)\u003c/i\u003e)\n\nSets the create *function* of this component generator, which will be invoked whenever a new component instance is created, being passed a *selection* containing the current DOM element, the current datum (*d*), and the index of the current datum (*i*).\n\n\u003ca href=\"#component_render\" name=\"component_render\" \u003e#\u003c/a\u003e \u003ci\u003ecomponent\u003c/i\u003e.\u003cb\u003erender\u003c/b\u003e(\u003ci\u003efunction(selection, d, i)\u003c/i\u003e)\n\nSets the render *function* of this component generator. This *function* will be invoked for each component instance during rendering, being passed a *selection* containing the current DOM element, the current datum (*d*), and the index of the current datum (*i*).\n\n\u003ca href=\"#component_destroy\" name=\"component_destroy\" \u003e#\u003c/a\u003e \u003ci\u003ecomponent\u003c/i\u003e.\u003cb\u003edestroy\u003c/b\u003e(\u003ci\u003efunction(selection, d, i)\u003c/i\u003e)\n\nSets the destroy *function* of this component generator, which will be invoked whenever a component instance is destroyed, being passed a *selection* containing the current DOM element, the current datum (*d*), and the index of the current datum (*i*).\n\nWhen a component instance gets destroyed, the destroy *function* of all its children is also invoked (recursively), so you can be sure that this *function* will be invoked before the compoent instance is removed from the DOM.\n\nThe destroy *function* may optionally return a transition, which will defer DOM element removal until after the transition is finished (but only if the parent component instance is not destroyed). Deeply nested component instances may have their DOM nodes removed before the transition completes, so it's best not to depend on the DOM node existing after the transition completes.\n\n\u003ca href=\"#component_key\" name=\"component_key\" \u003e#\u003c/a\u003e \u003ci\u003ecomponent\u003c/i\u003e.\u003cb\u003ekey\u003c/b\u003e(\u003ci\u003efunction\u003c/i\u003e)\n\nSets the key *function* used in the internal [data join](https://github.com/d3/d3-selection#selection_data) when managing DOM elements for component instances. Specifying a key *function* is optional (the array index is used as the key by default), but will make re-rendering more efficient in cases where *data* arrays get reordered or spliced over time.\n\n\u003ca href=\"#component_invoke\" name=\"component_invoke\" \u003e#\u003c/a\u003e \u003ci\u003ecomponent\u003c/i\u003e(\u003ci\u003eselection\u003c/i\u003e[,\u003ci\u003edata\u003c/i\u003e[,\u003ci\u003econtext\u003c/i\u003e]])\n\nRenders the component to the given *selection*, a D3 selection containing a single DOM element. A raw DOM element may also be passed in as the *selection* argument. Returns a D3 selection containing the merged Enter and Update selections for component instances.\n\n * If *data* is specified and is an array, one component instance will be rendered for each element of the array, and the *[render function](component_render)* will receive a single element of the *data* array as its *d* argument.\n   * **Useful case:** If *data* is specified as an empty array `[]`, all previously rendered component instances will be removed.\n * If *data* is specified and is not an array, exactly one component instance will be rendered, and the *[render function](component_render)* will receive the *data* value as its *d* argument.\n * If *data* is not specified, exactly one component instance will be rendered, and the *[render function](component_render)* will receive `undefined` as its *d* argument.\n\nIn summary, components can be rendered using the following signatures:\n\n * `selection.call(myComponent, dataObject)` → One instance, render function *d* will be `dataObject`.\n * `selection.call(myComponent, dataArray)` → `dataArray.length` instances, render function *d* will be `dataArray[i]`\n * `selection.call(myComponent)` → One instance, render function *d* will be `undefined`.\n\nIf a *context* object is specified, each data element in the data array will be shallow merged into a new object whose prototype is the *context* object, and the resulting array will be used in place of the *data* array. This is useful for passing down callback functions through your component tree. To clarify, the following two invocations are equivalent:\n\n```js\nvar context = {\n  onClick: function (){ console.log(\"Clicked!\");\n};\nselection.call(myComponent, dataArray.map(function (d){\n  return Object.assign(Object.create(context), d);\n}));\n```\n\n```js\nvar context = {\n  onClick: function (){ console.log(\"Clicked!\");\n};\nselection.call(myComponent, dataArray, context);\n```\n","funding_links":[],"categories":["Utils"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcurran%2Fd3-component","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcurran%2Fd3-component","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcurran%2Fd3-component/lists"}