{"id":"e257f81c745be447","slug":"collapsible-tree","first_public_version":369,"paused_version":505,"likes":395,"publish_level":"live","forks":891,"fork_of":null,"has_importers":true,"thumbnail":"681d16879a0e9077d3f2d72873aa067de6f5ca806a7bb6914c8c2441e5438057","default_thumbnail":"681d16879a0e9077d3f2d72873aa067de6f5ca806a7bb6914c8c2441e5438057","update_time":"2020-08-26T22:37:18.164Z","publish_time":"2023-06-15T08:49:55.853Z","publish_version":505,"latest_version":505,"roles":[],"sharing":null,"owner":{"id":"863951e3ebe4c0ae","avatar_url":"https://avatars.observableusercontent.com/avatar/5af16e327a90b2873351dda8a596c0d2d3bf954f64523deefe80177c9764d0f7","login":"d3","name":"D3","bio":"Bring your data to life.","home_url":"https://d3js.org","type":"team","tier":"pro_2024"},"creator":{"id":"074c414ad1d825f5","avatar_url":"https://avatars.observableusercontent.com/avatar/82811927da99f8938001b2ef1f552ad2c47083e46ebc55a3a146a5a5848c4519","login":"mbostock","name":"Mike Bostock","bio":"Visualization toolmaker. Founder @observablehq. Creator @d3. Former @nytgraphics. Pronounced BOSS-tock.","home_url":"https://bost.ocks.org/mike/","tier":"pro"},"authors":[{"id":"074c414ad1d825f5","avatar_url":"https://avatars.observableusercontent.com/avatar/82811927da99f8938001b2ef1f552ad2c47083e46ebc55a3a146a5a5848c4519","name":"Mike Bostock","login":"mbostock","bio":"Visualization toolmaker. Founder @observablehq. Creator @d3. Former @nytgraphics. Pronounced BOSS-tock.","home_url":"https://bost.ocks.org/mike/","tier":"pro","approved":true,"description":""}],"files":[{"id":"e65374209781891f37dea1e7a6e1c5e020a3009b8aedf113b4c80942018887a1176ad4945cf14444603ff91d3da371b3b0d72419fa8d2ee0f6e815732475d5de","url":"https://static.observableusercontent.com/files/e65374209781891f37dea1e7a6e1c5e020a3009b8aedf113b4c80942018887a1176ad4945cf14444603ff91d3da371b3b0d72419fa8d2ee0f6e815732475d5de","download_url":"https://static.observableusercontent.com/files/e65374209781891f37dea1e7a6e1c5e020a3009b8aedf113b4c80942018887a1176ad4945cf14444603ff91d3da371b3b0d72419fa8d2ee0f6e815732475d5de?response-content-disposition=attachment%3Bfilename*%3DUTF-8%27%27flare-2.json","name":"flare-2.json","create_time":"2019-10-29T23:05:26.680Z","mime_type":"application/json","status":"public","size":11634,"content_encoding":"gzip","private_bucket_id":null}],"comments":[],"commenting_lock":null,"suggestions_to":[],"suggestion_from":null,"collections":[{"id":"d9df4fb5263ace62","type":"public","slug":"visualization","title":"Visualization","description":"Explore and explain patterns in quantitative data using D3, Vega, and Observable Plot","update_time":"2023-07-19T17:50:40.230Z","pinned":true,"ordered":true,"custom_thumbnail":"09e385d95ce7df7d392b0133d68e97dd5675378190775d038d21516ea62178ba","default_thumbnail":"32ad600cf556f7b9991b2a11f7b6b8e55abe7ada6062ef88a5a7de422bb261ab","thumbnail":"09e385d95ce7df7d392b0133d68e97dd5675378190775d038d21516ea62178ba","listing_count":84,"parent_collection_count":1,"owner":{"id":"f35c755083683fe5","avatar_url":"https://avatars.observableusercontent.com/avatar/5a51c3b908225a581d20577e488e2aba8cbc9541c52982c638638c370c3e5e8e","login":"observablehq","name":"Observable","bio":"The end-to-end solution for building and hosting better data apps, dashboards, and reports.","home_url":"https://observablehq.com","type":"team","tier":"enterprise_2024"}},{"id":"1b0fbc3b1939d7fa","type":"public","slug":"gallery","title":"Gallery","description":"Examples featured in the D3 gallery","update_time":"2023-04-10T02:28:10.124Z","pinned":false,"ordered":false,"custom_thumbnail":null,"default_thumbnail":"6953adf7aec9d29339751fa68d9c9dc67169a0ffd7469f9a4055eda920bdd2a4","thumbnail":"6953adf7aec9d29339751fa68d9c9dc67169a0ffd7469f9a4055eda920bdd2a4","listing_count":147,"parent_collection_count":0,"owner":{"id":"863951e3ebe4c0ae","avatar_url":"https://avatars.observableusercontent.com/avatar/5af16e327a90b2873351dda8a596c0d2d3bf954f64523deefe80177c9764d0f7","login":"d3","name":"D3","bio":"Bring your data to life.","home_url":"https://d3js.org","type":"team","tier":"pro_2024"}},{"id":"ac3c6ed33ef3345f","type":"public","slug":"d3-hierarchy","title":"d3-hierarchy","description":"2D layout algorithms for visualizing hierarchical data.","update_time":"2019-03-07T07:26:12.604Z","pinned":true,"ordered":false,"custom_thumbnail":null,"default_thumbnail":"d1bb6653143239319cfee07a6f362c108ad0ddc9e7c064ebf185364992c2426c","thumbnail":"d1bb6653143239319cfee07a6f362c108ad0ddc9e7c064ebf185364992c2426c","listing_count":35,"parent_collection_count":1,"owner":{"id":"863951e3ebe4c0ae","avatar_url":"https://avatars.observableusercontent.com/avatar/5af16e327a90b2873351dda8a596c0d2d3bf954f64523deefe80177c9764d0f7","login":"d3","name":"D3","bio":"Bring your data to life.","home_url":"https://d3js.org","type":"team","tier":"pro_2024"}}],"version":360,"title":"Collapsible Tree","license":"isc","copyright":"Copyright 2018–2020 Observable, Inc.","nodes":[{"id":0,"value":"md`# Collapsible Tree\n\nClick a black node to expand or collapse [the tree](/@d3/tidy-tree).`","pinned":false,"mode":"js","data":null,"name":null},{"id":3,"value":"chart = {\n  const root = d3.hierarchy(data);\n\n  root.x0 = dy / 2;\n  root.y0 = 0;\n  root.descendants().forEach((d, i) => {\n    d.id = i;\n    d._children = d.children;\n    if (d.depth && d.data.name.length !== 7) d.children = null;\n  });\n\n  const svg = d3.create(\"svg\")\n      .attr(\"viewBox\", [-margin.left, -margin.top, width, dx])\n      .style(\"font\", \"10px sans-serif\")\n      .style(\"user-select\", \"none\");\n\n  const gLink = svg.append(\"g\")\n      .attr(\"fill\", \"none\")\n      .attr(\"stroke\", \"#555\")\n      .attr(\"stroke-opacity\", 0.4)\n      .attr(\"stroke-width\", 1.5);\n\n  const gNode = svg.append(\"g\")\n      .attr(\"cursor\", \"pointer\")\n      .attr(\"pointer-events\", \"all\");\n\n  function update(source) {\n    const duration = d3.event && d3.event.altKey ? 2500 : 250;\n    const nodes = root.descendants().reverse();\n    const links = root.links();\n\n    // Compute the new tree layout.\n    tree(root);\n\n    let left = root;\n    let right = root;\n    root.eachBefore(node => {\n      if (node.x < left.x) left = node;\n      if (node.x > right.x) right = node;\n    });\n\n    const height = right.x - left.x + margin.top + margin.bottom;\n\n    const transition = svg.transition()\n        .duration(duration)\n        .attr(\"viewBox\", [-margin.left, left.x - margin.top, width, height])\n        .tween(\"resize\", window.ResizeObserver ? null : () => () => svg.dispatch(\"toggle\"));\n\n    // Update the nodes…\n    const node = gNode.selectAll(\"g\")\n      .data(nodes, d => d.id);\n\n    // Enter any new nodes at the parent's previous position.\n    const nodeEnter = node.enter().append(\"g\")\n        .attr(\"transform\", d => `translate(${source.y0},${source.x0})`)\n        .attr(\"fill-opacity\", 0)\n        .attr(\"stroke-opacity\", 0)\n        .on(\"click\", (event, d) => {\n          d.children = d.children ? null : d._children;\n          update(d);\n        });\n\n    nodeEnter.append(\"circle\")\n        .attr(\"r\", 2.5)\n        .attr(\"fill\", d => d._children ? \"#555\" : \"#999\")\n        .attr(\"stroke-width\", 10);\n\n    nodeEnter.append(\"text\")\n        .attr(\"dy\", \"0.31em\")\n        .attr(\"x\", d => d._children ? -6 : 6)\n        .attr(\"text-anchor\", d => d._children ? \"end\" : \"start\")\n        .text(d => d.data.name)\n      .clone(true).lower()\n        .attr(\"stroke-linejoin\", \"round\")\n        .attr(\"stroke-width\", 3)\n        .attr(\"stroke\", \"white\");\n\n    // Transition nodes to their new position.\n    const nodeUpdate = node.merge(nodeEnter).transition(transition)\n        .attr(\"transform\", d => `translate(${d.y},${d.x})`)\n        .attr(\"fill-opacity\", 1)\n        .attr(\"stroke-opacity\", 1);\n\n    // Transition exiting nodes to the parent's new position.\n    const nodeExit = node.exit().transition(transition).remove()\n        .attr(\"transform\", d => `translate(${source.y},${source.x})`)\n        .attr(\"fill-opacity\", 0)\n        .attr(\"stroke-opacity\", 0);\n\n    // Update the links…\n    const link = gLink.selectAll(\"path\")\n      .data(links, d => d.target.id);\n\n    // Enter any new links at the parent's previous position.\n    const linkEnter = link.enter().append(\"path\")\n        .attr(\"d\", d => {\n          const o = {x: source.x0, y: source.y0};\n          return diagonal({source: o, target: o});\n        });\n\n    // Transition links to their new position.\n    link.merge(linkEnter).transition(transition)\n        .attr(\"d\", diagonal);\n\n    // Transition exiting nodes to the parent's new position.\n    link.exit().transition(transition).remove()\n        .attr(\"d\", d => {\n          const o = {x: source.x, y: source.y};\n          return diagonal({source: o, target: o});\n        });\n\n    // Stash the old positions for transition.\n    root.eachBefore(d => {\n      d.x0 = d.x;\n      d.y0 = d.y;\n    });\n  }\n\n  update(root);\n\n  return svg.node();\n}","pinned":true,"mode":"js","data":null,"name":null},{"id":323,"value":"diagonal = d3.linkHorizontal().x(d => d.y).y(d => d.x)","pinned":true,"mode":"js","data":null,"name":null},{"id":327,"value":"tree = d3.tree().nodeSize([dx, dy])","pinned":true,"mode":"js","data":null,"name":null},{"id":13,"value":"data = FileAttachment(\"flare-2.json\").json()","pinned":true,"mode":"js","data":null,"name":null},{"id":275,"value":"dx = 10","pinned":true,"mode":"js","data":null,"name":null},{"id":277,"value":"dy = width / 6","pinned":true,"mode":"js","data":null,"name":null},{"id":224,"value":"margin = ({top: 10, right: 120, bottom: 10, left: 40})","pinned":true,"mode":"js","data":null,"name":null},{"id":10,"value":"d3 = require(\"d3@6\")","pinned":true,"mode":"js","data":null,"name":null}],"resolutions":[],"schedule":null,"last_view_time":null}