{"id":13760494,"url":"https://github.com/gadfly361/rid3","last_synced_at":"2025-08-16T21:07:38.912Z","repository":{"id":62434445,"uuid":"88824484","full_name":"gadfly361/rid3","owner":"gadfly361","description":"Reagent Interface to D3","archived":false,"fork":false,"pushed_at":"2020-04-17T20:47:43.000Z","size":540,"stargazers_count":143,"open_issues_count":3,"forks_count":6,"subscribers_count":11,"default_branch":"master","last_synced_at":"2025-07-12T18:55:52.619Z","etag":null,"topics":["cljs","clojurescript","d3","d3js","reagent"],"latest_commit_sha":null,"homepage":"","language":"Clojure","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/gadfly361.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGES.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2017-04-20T05:29:34.000Z","updated_at":"2025-05-26T19:00:48.000Z","dependencies_parsed_at":"2022-11-01T21:16:05.578Z","dependency_job_id":null,"html_url":"https://github.com/gadfly361/rid3","commit_stats":null,"previous_names":[],"tags_count":6,"template":false,"template_full_name":null,"purl":"pkg:github/gadfly361/rid3","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gadfly361%2Frid3","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gadfly361%2Frid3/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gadfly361%2Frid3/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gadfly361%2Frid3/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/gadfly361","download_url":"https://codeload.github.com/gadfly361/rid3/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gadfly361%2Frid3/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":270770475,"owners_count":24642150,"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","status":"online","status_checked_at":"2025-08-16T02:00:11.002Z","response_time":91,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"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":["cljs","clojurescript","d3","d3js","reagent"],"created_at":"2024-08-03T13:01:11.476Z","updated_at":"2025-08-16T21:07:38.862Z","avatar_url":"https://github.com/gadfly361.png","language":"Clojure","funding_links":[],"categories":["Clojure"],"sub_categories":[],"readme":"# rid3\n\nRid3: [**R**eagent](https://github.com/reagent-project/reagent) **i**nterface to [**d3**](https://d3js.org/). Pronounced like the word 'ride'.\n\n[basics](http://rawgit.com/gadfly361/rid3/master/dev-resources/public/basics.html)\n\n[examples](http://rawgit.com/gadfly361/rid3/master/dev-resources/public/examples.html)\n\n[clojure meetup slides](http://rawgit.com/gadfly361/rid3/master/docs/cph_meetup/rid3_intro.html) based on v0.1.0-alpha-3.\n\nTo use rid3, add the following to the `:dependencies` vector in your project.clj file:\n\n```clojure\n[rid3 \"0.2.2\"]\n```\n\nUsing an older version?\n\n- [Changes](https://github.com/gadfly361/rid3/blob/master/CHANGES.md)\n- [Previous docs](https://github.com/gadfly361/rid3/tree/master/docs/READMEs)\n\n## The Problem\n\nIn my experience, there is a lot of boilerplate involved when trying to get reagent (i.e., react) and d3 to play nicely together.  The crux of the problem is you only want to append containing g tags (or static elements) to the DOM during reagent's `component-did-mount` lifecycle method and not during the `component-did-update` lifecycle method. However, more often than not, you want d3 to modify the stuff contained in the g tag in the same manner whether or not the component just mounted or just updated.\n\n## Rid3's Solution\n\nRid3 exposes a single reagent component, `viz`, with the aim to make 80% of what you'll likely want to make with d3 easier (bar charts, pie charts, scatter plots, etc.).  Rid3 attempts to achieve this by making containing g tags for you. The benefit of doing this is that you can now use the *same* function in the did-mount and did-update lifecycle methods.  However, rid3 goes a step further and will *default* the did-update lifecycle method to whatever you supply as the did-mount lifecycle method.\n\n## Viz Component\n\nThe `viz` component takes a hash-map of the following:\n\n| key              | type               | default                                         | required? |\n|------------------|--------------------|-------------------------------------------------|-----------|\n| :id              | string             |                                                 | yes       |\n| :ratom           | reagent.core/atom  |                                                 | yes       |\n| :svg             | svg                |                                                 | yes       |\n| :main-container  | main-container     |                                                 | no        |\n| :pieces          | [ piece ]          |                                                 | no        |\n| :class           | string             |                                                 | no        |\n\n- an `:id` is required to differentiate between different rid3's\n- a `:ratom` can be a reagent atom, reagent cursor, or a re-frame subscription. This should be used to store all of the data relevant to your rid3.\n\n### :svg\n\nWhere an svg hash-map looks like:\n\n| key         | type                  | default   | required? |\n|-------------|-----------------------|-----------|-----------|\n| :did-mount  | (fn [node ratom] ...) |           | yes       |\n| :did-update | (fn [node ratom] ...) | did-mount | no        |\n\n\n### :main-container\n\nWhere a main-container hash-map looks like:\n\n| key         | type                  | default   | required? |\n|-------------|-----------------------|-----------|-----------|\n| :did-mount  | (fn [node ratom] ...) |           | no        |\n| :did-update | (fn [node ratom] ...) | did-mount | no        |\n\n### :pieces\n\nAnd where :pieces is a vector of piece hash-maps.  There are four kinds of piece hash-maps:\n\n**`:container`** for when you want to group *elem* or *elem-with-data* pieces under the same g tag.\n\n| key         | type                  | default   | required? |\n|-------------|-----------------------|-----------|-----------|\n| :class      | string                |           | yes       |\n| :did-mount  | (fn [node ratom] ...) |           | yes       |\n| :did-update | (fn [node ratom] ...) | did-mount | no        |\n| :children   | [ piece ]             |           | no        |\n\n**`:elem`** for when you want to add an element like a text or a circle.\n\n| key         | type                  | default   | required? |\n|-------------|-----------------------|-----------|-----------|\n| :class      | string                |           | yes       |\n| :tag        | string                |           | yes       |\n| :did-mount  | (fn [node ratom] ...) |           | yes       |\n| :did-update | (fn [node ratom] ...) | did-mount | no        |\n\n**`:elem-with-data`** for when you want to add a series of elements that are joined to a dataset.\n\n| key              | type                  | default                                         | required? |\n|------------------|-----------------------|-------------------------------------------------|-----------|\n| :class           | string                |                                                 | yes       |\n| :tag             | string                |                                                 | yes       |\n| :did-mount       | (fn [node ratom] ...) |                                                 | yes (1)   |\n| :did-update      | (fn [node ratom] ...) | did-mount                                       | no        |\n| :gup             | gup-hash-map          |                                                 | yes (1)   |\n| :prepare-dataset | (fn [ratom] ...)      | (fn [ratom] (-\u003e @ratom (get :dataset) clj-\u003ejs)) | no        |\n| :key-fn          | (fn [d i] ...)        |                                                 | no        |\n\n\n - `:elem-with-data` pieces expect what is returned by the\n   `:prepare-dataset` function to be a JavaScript array. E.g. `[1, 2, 3]` or\n   `[{\"color\": \"blue\"}, {\"color\": \"green\"} ... ]`\n   \n - If `:prepare-dataset` is not provided, then (as a default)`:elem-with-data` will convert whatever is stored in the `:dataset` key of your `ratom` to a JavaScript array.\n\n - Individual datums (often referred to as \"d\") of the JavaScript array are passed to the anonymous functions that\n   can be used to set properties of each `:elem-with-data` element. E.g. `(.attr node \"color\"\n   (fn [d] (goog.object/get d \"color\")))`\n   \n- (1) You can use either `:did-mount` and `:did-update` **or** `:gup`. You *cannot* mix.\n\n- If you choose to use `:gup`, then you can explicitly set attributes and add transitions in the `:enter-init`, `:enter`, `:update`, and `:exit` parts of the [general update pattern](https://bl.ocks.org/mbostock/3808234).\n\n- A `gup-hash-map` looks like:\n\n```clojure\n{:enter-init  (fn [node ratom] ...)\n :enter       (fn [node ratom] ...)\n :update      (fn [node ratom] ...)\n :exit        (fn [node ratom] ...)}\n```\n\n- Note: only use `:enter-init` if you want a *special* enter transition when the vizusualiztion first mounts, otherwise, you can ignore it, and just use `:enter`, `:update` and `:exit`.\n\n**`:raw`** for when you want to either trigger some side-effect or have an escape hatch from the rid3\n\n| key         | type             | default | required? |\n|-------------|------------------|---------|-----------|\n| :did-mount  | (fn [ratom] ...) |         | yes       |\n| :did-update | (fn [ratom] ...) |         | yes       |\n\n## Joy Rid3 (Minimal Example)\n\nJust so you can get a sense of a `viz`component, let's create a minimal example ...  a circle with text on top of it.\n\n### Add rid3 to your project\n\nAdd the following to the `:dependencies` vector of your project.clj file.\n\n```clojure\n[rid3 \"0.2.2\"]\n```\n\n### Require rid3 in your namespace\n\n```clojure\n(ns foo.core\n  (:require\n   [rid3.core :as rid3 :refer [rid3-\u003e]]))\n```\n\n### Create an svg\n\n```clojure\n(defn viz [ratom]\n  [rid3/viz\n   {:id    \"some-id\"\n    :ratom ratom\n    :svg   {:did-mount (fn [node ratom]\n                         (rid3-\u003e node\n                                 {:width  200\n                                  :height 200\n                                  :style  {:background-color \"grey\"}}))}\n    }])\n```\n\nWhich will result in the following:\n\n```html\n\u003cdiv id=\"some-id\"\u003e\n  \u003csvg width=\"200\" height=\"200\" style=\"background-color: grey;\"\u003e\n    \u003cg class=\"rid3-main-container\"\u003e\n    \u003c/g\u003e\n  \u003c/svg\u003e\n\u003c/div\u003e\n```\n\n*Note: All viz components need to provide a ratom. All relevant data for the component should be stored here. If anything changes in this ratom, then rid3 will trigger a re-render of the viz component for you.*\n\nYou probably noticed that rid3 added a g tag with the class `.rid3-main-container` inside of your svg.  This is where rid3 will place all of your pieces.\n\nYou probably also noticed the use of `rid3-\u003e`. This is just syntactic sugar to set attributes with familiar hiccup-like attribute maps.  The above is the same as doing the following:\n\n```clojure\n(defn viz [ratom]\n  [rid3/viz\n   {:id    \"some-id\"\n    :ratom ratom\n    :svg   {:did-mount (fn [node ratom]\n                         (-\u003e node\n                             (.attr \"width\" 200)\n                             (.attr \"height\" 200)\n                             (.style \"background-color\" \"grey\")))}\n    }])\n```\n\n### Add a circle\n\n```clojure\n(defn viz [ratom]\n  [rid3/viz\n   {:id    \"some-id\"\n    :ratom ratom\n    :svg   {:did-mount (fn [node ratom]\n                         (rid3-\u003e node\n                                 {:width  200\n                                  :height 200\n                                  :style  {:background-color \"grey\"}}))}\n    ;; ATTENTION \\/\n    :pieces\n    [{:kind      :elem\n      :class     \"backround\"\n      :tag       \"circle\"\n      :did-mount (fn [node ratom]\n                   (rid3-\u003e node\n                           {:cx 100\n                            :cy 100\n                            :r  50}))}]\n    }])\n```\n\nWhich will result in the following:\n\n```html\n\u003cdiv id=\"some-id\"\u003e\n  \u003csvg width=\"200\" height=\"200\" style=\"background-color: grey;\"\u003e\n    \u003cg class=\"rid3-main-container\"\u003e\n      \u003cg class=\"backround\"\u003e\n\t\u003ccircle cx=\"100\" cy=\"100\" r=\"50\"\u003e\n\t\u003c/circle\u003e\n      \u003c/g\u003e\n    \u003c/g\u003e\n  \u003c/svg\u003e\n\u003c/div\u003e\n```\n\nPlease note, that there is **no** `.append` method in our did-mount function! Rid3 created a containing g tag and placed the circle inside of it for us.  The node that is passed to the did-mount function is the circle itself.  You can confirm this by looking at where the attributes from our did-mount function are added.\n\n### Add text\n\n```clojure\n(defn viz [ratom]\n  [rid3/viz\n   {:id    \"some-id\"\n    :ratom ratom\n    :svg   {:did-mount (fn [node ratom]\n                         (rid3-\u003e node\n                                 {:width  200\n                                  :height 200\n                                  :style  {:background-color \"grey\"}}))}\n    :pieces\n    [{:kind      :elem\n      :class     \"backround\"\n      :tag       \"circle\"\n      :did-mount (fn [node ratom]\n                   (rid3-\u003e node\n                           {:cx 100\n                            :cy 100\n                            :r  50}))}\n     ;; ATTENTION \\/\n     {:kind      :elem\n      :class     \"foreground\"\n      :tag       \"text\"\n      :did-mount (fn [node ratom]\n                   (rid3-\u003e node\n                           {:x                  100\n                            :y                  100\n                            :text-anchor        \"middle\"\n                            :alignment-baseline \"middle\"\n                            :fill               \"green\"\n                            :font-size          \"24px\"\n                            :font-family        \"sans-serif\"}\n                           (.text \"RID3\")))}]\n    }])\n```\t\n\nWhich will result in the following:\n\n```html\n\u003cdiv id=\"some-id\"\u003e\n  \u003csvg width=\"200\" height=\"200\" style=\"background-color: grey;\"\u003e\n    \u003cg class=\"rid3-main-container\"\u003e\n      \u003cg class=\"backround\"\u003e\n\t\u003ccircle cx=\"100\" cy=\"100\" r=\"50\"\u003e\n\t\u003c/circle\u003e\n      \u003c/g\u003e\n      \u003cg class=\"foreground\"\u003e\n\t\u003ctext x=\"100\" y=\"100\" text-anchor=\"middle\" alignment-baseline=\"middle\" fill=\"green\" font-size=\"24px\" font-family=\"sans-serif\"\u003eRID3\n\t\u003c/text\u003e\n      \u003c/g\u003e\n    \u003c/g\u003e\n  \u003c/svg\u003e\n\u003c/div\u003e\n```\n\n*Note: the order of pieces matters.  If we reversed the order of our text and circle, we wouldn't be able to see the text (because it would be behind the circle).*\n\n---\n\nDon't forget to check out the following:\n\n[basics](http://rawgit.com/gadfly361/rid3/master/dev-resources/public/basics.html)\n\n[examples](http://rawgit.com/gadfly361/rid3/master/dev-resources/public/examples.html)\n\n## License\n\n```\nThe MIT License (MIT)\n\nCopyright © 2017 Matthew Jaoudi\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgadfly361%2Frid3","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fgadfly361%2Frid3","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgadfly361%2Frid3/lists"}