23 | );
24 | }
25 |
26 | export default BarChart;
27 |
--------------------------------------------------------------------------------
/standalone/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright 2019–2020 Observable, Inc.
2 |
3 | Permission to use, copy, modify, and/or distribute this software for any
4 | purpose with or without fee is hereby granted, provided that the above
5 | copyright notice and this permission notice appear in all copies.
6 |
7 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
8 | WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
9 | MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
10 | ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
11 | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
12 | ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
13 | OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
--------------------------------------------------------------------------------
/react-create-react-app/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright 2018–2020 Observable, Inc.
2 |
3 | Permission to use, copy, modify, and/or distribute this software for any
4 | purpose with or without fee is hereby granted, provided that the above
5 | copyright notice and this permission notice appear in all copies.
6 |
7 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
8 | WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
9 | MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
10 | ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
11 | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
12 | ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
13 | OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
14 |
--------------------------------------------------------------------------------
/standalone/bar-chart-race/LICENSE.txt:
--------------------------------------------------------------------------------
1 | Copyright 2019–2020 Observable, Inc.
2 |
3 | Permission to use, copy, modify, and/or distribute this software for any
4 | purpose with or without fee is hereby granted, provided that the above
5 | copyright notice and this permission notice appear in all copies.
6 |
7 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
8 | WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
9 | MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
10 | ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
11 | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
12 | ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
13 | OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
14 |
--------------------------------------------------------------------------------
/react-dataflow/App.js:
--------------------------------------------------------------------------------
1 | import React, {useState} from "react";
2 | import BrushableScatterplot from "./BrushableScatterplot.js";
3 |
4 | export const App = () => {
5 | // passed to notebook
6 | const [height, setHeight] = useState(600)
7 |
8 | // setter passed to notebook so it can set and this component can render
9 | const [selection, setSelection] = useState([])
10 |
11 | return (
12 | <>
13 |
14 |
15 |
Height of chart: setHeight(e.target.value)} />
16 |
Current selection: {selection.length ? selection.map(d => d.name).join(", ") : "empty"}
17 |
18 | >
19 | );
20 | }
21 |
--------------------------------------------------------------------------------
/react-dataflow-dynamic/App.js:
--------------------------------------------------------------------------------
1 | import React, {useState} from "react";
2 | import BrushableScatterplot from "./BrushableScatterplot.js";
3 |
4 | export const App = () => {
5 | // passed to notebook
6 | const [height, setHeight] = useState(600)
7 |
8 | // setter passed to notebook so it can set and this component can render
9 | const [selection, setSelection] = useState([])
10 |
11 | return (
12 | <>
13 |
14 |
15 |
Height of chart: setHeight(e.target.value)} />
16 |
Current selection: {selection.length ? selection.map(d => d.name).join(", ") : "empty"}
17 |
18 | >
19 | );
20 | }
21 |
--------------------------------------------------------------------------------
/react-create-react-app/src/@d3/zoomable-sunburst/LICENSE.txt:
--------------------------------------------------------------------------------
1 | Copyright 2020 Observable, Inc.
2 |
3 | Permission to use, copy, modify, and/or distribute this software for any
4 | purpose with or without fee is hereby granted, provided that the above
5 | copyright notice and this permission notice appear in all copies.
6 |
7 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
8 | WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
9 | MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
10 | ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
11 | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
12 | ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
13 | OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
14 |
--------------------------------------------------------------------------------
/react-dataflow-dynamic/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "simple2",
3 | "version": "1.0.0",
4 | "description": "",
5 | "main": "index.js",
6 | "scripts": {
7 | "test": "echo \"Error: no test specified\" && exit 1",
8 | "bundle": "rollup -c"
9 | },
10 | "keywords": [],
11 | "author": "",
12 | "license": "ISC",
13 | "devDependencies": {
14 | "@babel/core": "^7.12.10",
15 | "@babel/preset-react": "^7.12.10",
16 | "@rollup/plugin-babel": "^5.2.2",
17 | "@rollup/plugin-commonjs": "^17.0.0",
18 | "@rollup/plugin-node-resolve": "^11.1.0",
19 | "@rollup/plugin-replace": "^2.3.4",
20 | "rollup": "^2.36.1",
21 | "rollup-plugin-terser": "^7.0.2"
22 | },
23 | "dependencies": {
24 | "@observablehq/runtime": "^4.18.6",
25 | "react": "^17.0.1",
26 | "react-dom": "^17.0.1"
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/react-dataflow/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
17 |
This example demonstrates embedding an Observable notebook as a React component with two-way dataflow. If you brush the scatterplot below, the selected data will be displayed. Likewise if you edit the height input below, controlled by React, the chart will automatically resize.
19 |
20 |
21 |
--------------------------------------------------------------------------------
/iframe-resize/README.md:
--------------------------------------------------------------------------------
1 | # Observable Example: Iframe Resize
2 |
3 | See it live: https://observablehq.github.io/examples/iframe-resize/
4 |
5 | oEmbed is an open specification letting websites discover how to embed content given only a URL. The embed is done by an iframe, but iframes do not naturally resize to fit their contents; on different screen sizes, or as content changes, there may be different amounts of the content inside visible. Embedly, which powers oEmbed on sites like Medium (which runs it), [specifics a protocol](https://docs.embed.ly/v1.0/docs/provider-height-resizing) for letting iframes resize themselves: [send a message](https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage) with the desired height. If you’re using the Iframe embed and would like to take advantage of automatic resizing, you can implement a message listener like this one.
6 |
--------------------------------------------------------------------------------
/react-dataflow-dynamic/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
17 |
This example demonstrates embedding an Observable notebook as a React component with two-way dataflow. If you brush the scatterplot below, the selected data will be displayed. Likewise if you edit the height input below, controlled by React, the chart will automatically resize.
19 |
20 |
21 |
--------------------------------------------------------------------------------
/react-dataflow-dynamic/rollup.config.js:
--------------------------------------------------------------------------------
1 | import babel from "@rollup/plugin-babel";
2 | import commonjs from "@rollup/plugin-commonjs";
3 | import resolve from "@rollup/plugin-node-resolve";
4 | import replace from "@rollup/plugin-replace";
5 | import {terser} from "rollup-plugin-terser";
6 |
7 | export default {
8 | input: "index.js",
9 | output: {
10 | file: "bundle.js",
11 | format: "iife"
12 | },
13 | plugins: [
14 | resolve(),
15 | commonjs({
16 | include: "node_modules/**"
17 | }),
18 | // This is for our app source code, which uses jsx
19 | babel({
20 | presets: ["@babel/preset-react"],
21 | exclude: "node_modules/**", // only transpile jsx in our source code
22 | babelHelpers: "bundled"
23 | }),
24 | replace({
25 | "process.env.NODE_ENV": JSON.stringify("production")
26 | }),
27 | terser()
28 | ]
29 | }
30 |
--------------------------------------------------------------------------------
/simple-notebook/README.md:
--------------------------------------------------------------------------------
1 | # Observable Example: Simple Notebook
2 |
3 | See it live: https://observablehq.github.io/examples/simple-notebook/
4 |
5 | This bare-bones example demonstrates rendering an Observable-hosted [example notebook](https://observablehq.com/@jashkenas/my-neat-notebook) into the document body.
6 |
7 | ```js
8 | // Load the Observable runtime and inspector.
9 | import {Runtime, Inspector} from "https://cdn.jsdelivr.net/npm/@observablehq/runtime@4/dist/runtime.js";
10 |
11 | // Your notebook, compiled as an ES module.
12 | import notebook from "https://api.observablehq.com/@jashkenas/my-neat-notebook.js?v=3";
13 |
14 | // Load the notebook, observing its cells with a default Inspector that simply
15 | // renders the value of every cell into the provided DOM node.
16 | const runtime = new Runtime();
17 | const main = runtime.module(notebook, Inspector.into(document.body));
18 | ```
19 |
--------------------------------------------------------------------------------
/standalone/bar-chart-race/README.md:
--------------------------------------------------------------------------------
1 | # Bar Chart Race
2 |
3 | https://observablehq.com/@d3/bar-chart-race@3048
4 |
5 | View this notebook in your browser by running a web server in this folder. For
6 | example:
7 |
8 | ~~~sh
9 | python -m SimpleHTTPServer
10 | ~~~
11 |
12 | Or, use the [Observable Runtime](https://github.com/observablehq/runtime) to
13 | import this module directly into your application. To npm install:
14 |
15 | ~~~sh
16 | npm install @observablehq/runtime@4
17 | npm install https://api.observablehq.com/@d3/bar-chart-race.tgz?v=3
18 | ~~~
19 |
20 | Then, import your notebook and the runtime as:
21 |
22 | ~~~js
23 | import {Runtime, Inspector} from "@observablehq/runtime";
24 | import define from "@d3/bar-chart-race";
25 | ~~~
26 |
27 | To log the value of the cell named “foo”:
28 |
29 | ~~~js
30 | const runtime = new Runtime();
31 | const main = runtime.module(define);
32 | main.value("foo").then(value => console.log(value));
33 | ~~~
34 |
--------------------------------------------------------------------------------
/react-create-react-app/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "react-zoomable-sunburst",
3 | "version": "0.1.0",
4 | "license": "ISC",
5 | "dependencies": {
6 | "@observablehq/runtime": "^4.7.3",
7 | "@testing-library/jest-dom": "^4.2.4",
8 | "@testing-library/react": "^9.3.2",
9 | "@testing-library/user-event": "^7.1.2",
10 | "react": "^16.13.1",
11 | "react-dom": "^16.13.1",
12 | "react-scripts": "3.4.1"
13 | },
14 | "scripts": {
15 | "start": "react-scripts start",
16 | "build": "react-scripts build",
17 | "test": "react-scripts test",
18 | "eject": "react-scripts eject"
19 | },
20 | "eslintConfig": {
21 | "extends": "react-app"
22 | },
23 | "browserslist": {
24 | "production": [
25 | ">0.2%",
26 | "not dead",
27 | "not op_mini all"
28 | ],
29 | "development": [
30 | "last 1 chrome version",
31 | "last 1 firefox version",
32 | "last 1 safari version"
33 | ]
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/react-file-attachment/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "simple2",
3 | "version": "1.0.0",
4 | "description": "",
5 | "main": "index.js",
6 | "scripts": {
7 | "test": "echo \"Error: no test specified\" && exit 1",
8 | "bundle": "mkdir -p files && cp -f node_modules/@d3/bar-chart/files/* files && rollup -c"
9 | },
10 | "keywords": [],
11 | "author": "",
12 | "license": "ISC",
13 | "devDependencies": {
14 | "@babel/core": "^7.12.10",
15 | "@babel/preset-env": "^7.12.11",
16 | "@babel/preset-react": "^7.12.10",
17 | "@rollup/plugin-babel": "^5.2.2",
18 | "@rollup/plugin-commonjs": "^17.0.0",
19 | "@rollup/plugin-node-resolve": "^11.1.0",
20 | "@rollup/plugin-replace": "^2.3.4",
21 | "babel-plugin-bundled-import-meta": "^0.3.2",
22 | "rollup": "^2.36.1",
23 | "rollup-plugin-terser": "^7.0.2"
24 | },
25 | "dependencies": {
26 | "@d3/bar-chart": "https://api.observablehq.com/@d3/bar-chart@262.tgz?v=3",
27 | "@observablehq/runtime": "^4.8.0",
28 | "react": "^17.0.1",
29 | "react-dom": "^17.0.1"
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/custom-fluid-width/README.md:
--------------------------------------------------------------------------------
1 | # Observable Example: Custom Fluid Width
2 |
3 | See it live: https://observablehq.github.io/examples/custom-fluid-width/
4 |
5 | By default, `width` in an embedded notebook resolves to the [body’s client width](https://github.com/observablehq/stdlib/blob/master/src/width.js). Depending on where you embed a cell, you may wish redefine the value of `width`, *e.g.* to fit the cell’s container. We can accomplish this by [redefining](https://github.com/observablehq/runtime/blob/master/README.md#module_redefine) `width` in the embedded notebook as a [Generator](https://observablehq.com/@observablehq/introduction-to-generators) that listens for window *resize* events and yields a new value if the client width of the container has changed. (See the [custom fluid width and height example](../custom-fluid-width-and-height/) for an alternative that uses ResizeObserver.)
6 |
7 | Redefining `width` only affects the embedded notebook; it doesn’t change the value of `width` provided by the standard library in transitive imports. For that, see the [custom library example](../custom-library).
8 |
--------------------------------------------------------------------------------
/react-dataflow/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "simple2",
3 | "version": "1.0.0",
4 | "description": "",
5 | "main": "index.js",
6 | "scripts": {
7 | "test": "echo \"Error: no test specified\" && exit 1",
8 | "bundle": "mkdir -p files && cp -f node_modules/@d3/brushable-scatterplot/files/* files && rollup -c"
9 | },
10 | "keywords": [],
11 | "author": "",
12 | "license": "ISC",
13 | "devDependencies": {
14 | "@babel/core": "^7.12.10",
15 | "@babel/preset-env": "^7.12.11",
16 | "@babel/preset-react": "^7.12.10",
17 | "@rollup/plugin-babel": "^5.2.2",
18 | "@rollup/plugin-commonjs": "^17.0.0",
19 | "@rollup/plugin-node-resolve": "^11.1.0",
20 | "@rollup/plugin-replace": "^2.3.4",
21 | "babel-plugin-bundled-import-meta": "^0.3.2",
22 | "rollup": "^2.36.1",
23 | "rollup-plugin-terser": "^7.0.2"
24 | },
25 | "dependencies": {
26 | "@d3/brushable-scatterplot": "https://api.observablehq.com/@d3/brushable-scatterplot@213.tgz?v=3",
27 | "@observablehq/runtime": "^4.8.0",
28 | "react": "^17.0.1",
29 | "react-dom": "^17.0.1"
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/custom-fluid-width-and-height/README.md:
--------------------------------------------------------------------------------
1 | # Observable Example: Custom Fluid Width & Height
2 |
3 | See it live: https://observablehq.github.io/examples/custom-fluid-width-and-height/
4 |
5 | By default, `width` in an embedded notebook resolves to the [body’s client width](https://github.com/observablehq/stdlib/blob/master/src/width.js). Many notebooks also define a fixed `height` cell. Depending on where you embed a cell, you may wish redefine the value of `width` and `height`, *e.g.* to fit the cell’s container. We can accomplish this by [redefining](https://github.com/observablehq/runtime/blob/master/README.md#module_redefine) `width` and `height` in the embedded notebook as a [Generator](https://observablehq.com/@observablehq/introduction-to-generators) that uses a ResizeObserver and yields a new value when the size of the container changes. (See the [custom fluid width example](../custom-fluid-width/) for an alternative that use the window *resize* event.)
6 |
7 | Redefining `width` and `height` only affects the embedded notebook; it doesn’t change the value of `width` provided by the standard library in transitive imports. For that, see the [custom library example](../custom-library).
8 |
--------------------------------------------------------------------------------
/react-file-attachment/README.md:
--------------------------------------------------------------------------------
1 | # Observable Example: React + FileAttachment
2 |
3 | See it live: https://observablehq.github.io/examples/react-file-attachment/
4 |
5 | This Rollup configuration supports file attachments, whereas many React environments (like the one you get from `create-react-app`) stumble on file attachments’ use of import.meta.
6 |
7 | ```jsx
8 | function BarChart() {
9 | const ref = useRef();
10 |
11 | useEffect(() => {
12 | const runtime = new Runtime();
13 | runtime.module(notebook, name => {
14 | if (name === "chart") {
15 | return new Inspector(ref.current);
16 | }
17 | });
18 | return () => runtime.dispose();
19 | }, []);
20 |
21 | return (
22 |
26 | );
27 | }
28 | ```
29 |
30 | See also [the create-react-app example](../react-create-react-app), which edits the downloaded code directly to avoid the use of import.meta, and the more advanced [React + dataflow example](../react-dataflow/) for coordinating state between React and Observable.
31 |
--------------------------------------------------------------------------------
/custom-library/README.md:
--------------------------------------------------------------------------------
1 | # Observable Example: Custom Library
2 |
3 | See it live: https://observablehq.github.io/examples/custom-library/
4 |
5 | The [Observable standard library](https://github.com/observablehq/stdlib/blob/master/README.md) provides standard functionality to all Observable notebooks, such as [responsive width](https://github.com/observablehq/stdlib/blob/master/README.md#width) and [require](https://github.com/observablehq/stdlib/blob/master/README.md#require) for loading libraries.
6 |
7 | This example demonstrates how to redefine the standard library when embedding, allowing you to redefine its behavior both in the embedded notebook and any transitive imports. Here we’ll override the [md tagged template literal](https://github.com/observablehq/stdlib/blob/master/README.md#md) to use [markdown-it](https://github.com/markdown-it/markdown-it) and enable automatic typographic quotes.
8 |
9 | See also the [custom fluid width example](../custom-fluid-width/) for a slightly simpler technique to redefine variables directly in the embedded notebook, and the [custom library resolver example](../custom-library-resolve/) if you just wish to change how required libraries are resolved.
10 |
--------------------------------------------------------------------------------
/simple-constant/README.md:
--------------------------------------------------------------------------------
1 | # Observable Example: Simple Constant
2 |
3 | See it live: https://observablehq.github.io/examples/simple-constant/
4 |
5 | This example demonstrates extracting values from an [example notebook](https://observablehq.com/d/e893bb16368a0a92) using [*module*.value](https://github.com/observablehq/runtime/blob/master/README.md#module_value). This is convenient when the value of the cell never changes; see the [simple generator example](../simple-generator/) for an alternative technique that allows observing values that change over time.
6 |
7 | ```js
8 | import {Runtime, Inspector} from "https://cdn.jsdelivr.net/npm/@observablehq/runtime@4/dist/runtime.js";
9 | import notebook from "https://api.observablehq.com/d/e893bb16368a0a92.js?v=3";
10 |
11 | (async () => {
12 | const runtime = new Runtime();
13 | const main = runtime.module(notebook);
14 |
15 | // Extract the value of “answer”.
16 | const answer = await main.value("answer");
17 | console.log(`The answer is ${answer}.`); // The answer is 42.
18 |
19 | // Extract the value of “greet”.
20 | const greet = await main.value("greet");
21 | console.log(greet("world")); // Hello, world!
22 | })();
23 | ```
24 |
--------------------------------------------------------------------------------
/custom-fixed-width/README.md:
--------------------------------------------------------------------------------
1 | # Observable Example: Custom Fixed Width
2 |
3 | See it live: https://observablehq.github.io/examples/custom-fixed-width/
4 |
5 | By default, `width` in an embedded notebook resolves to the [body’s client width](https://github.com/observablehq/stdlib/blob/master/src/width.js). Depending on where you embed a cell, you may wish redefine the value of `width`, *e.g.* to fit the cell’s container. We can accomplish this by [redefining](https://github.com/observablehq/runtime/blob/master/README.md#module_redefine) `width` in the embedded notebook as a fixed value such as 640px. (See also the [custom fluid width](../custom-fluid-width/) and [custom fluid width and height](../custom-fluid-width-and-height/) examples.)
6 |
7 | Note that in some notebooks, `width` is only used to set an SVG’s viewBox attribute, which does not set the size of the SVG on the page; it merely affects the SVG’s internal coordinate system. In this case you may need to both redefine `width` and explicitly style the container to have the desired width.
8 |
9 | Redefining `width` only affects the embedded notebook; it doesn’t change the value of `width` provided by the Observable standard library in transitive imports. For that, see the [custom library example](../custom-library).
10 |
--------------------------------------------------------------------------------
/simple-notebook/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | Observable Example: Simple Notebook
4 |
18 |
22 |
23 |
41 |
--------------------------------------------------------------------------------
/custom-data/README.md:
--------------------------------------------------------------------------------
1 | # Observable Example: Custom Data
2 |
3 | See it live: https://observablehq.github.io/examples/custom-data/
4 |
5 | When you embed an Observable notebook, you can also override any cell with your own values using [*module*.redefine](https://github.com/observablehq/runtime/blob/master/README.md#module_redefine). For example:
6 |
7 | ```js
8 | // Redefine the cell “data” as the contents of the local file population.json;
9 | // this will affect the chart embedded above.
10 | main.redefine("data", fetch("population.json").then(response => response.json()));
11 | ```
12 |
13 | This is akin to doing
14 |
15 | ```js
16 | import {chart} with {myData as data} from "@d3/zoomable-sunburst"
17 | ```
18 |
19 | within a notebook.
20 |
21 | This example shows a [Zoomable Sunburst](https://observablehq.com/@d3/zoomable-sunburst) where the data has been replaced by the local `population.json`, which uses [data from the Census Bureau](https://www.census.gov/data/datasets/time-series/demo/popest/2010s-counties-total.html). Note that here `data` is redefined as a _promise_, and the chart renders with the data as soon as it resolves!
22 |
23 | The data was previously converted from a flat CSV (with columns for name, parent, and value) to a hierarchy using [d3.stratify](https://observablehq.com/@d3/d3-stratify), and the hierarchy was then [saved to JSON](https://observablehq.com/@tophtucker/d3-hierarchy-to-json).
24 |
25 | See also the [custom live data example](../custom-live-data/).
26 |
--------------------------------------------------------------------------------
/standalone/bar-chart-race/inspector.css:
--------------------------------------------------------------------------------
1 | :root{--syntax_normal:#1b1e23;--syntax_comment:#a9b0bc;--syntax_number:#20a5ba;--syntax_keyword:#c30771;--syntax_atom:#10a778;--syntax_string:#008ec4;--syntax_error:#ffbedc;--syntax_unknown_variable:#838383;--syntax_known_variable:#005f87;--syntax_matchbracket:#20bbfc;--syntax_key:#6636b4;--mono_fonts:82%/1.5 Menlo,Consolas,monospace}.observablehq--collapsed,.observablehq--expanded,.observablehq--function,.observablehq--gray,.observablehq--import,.observablehq--string:after,.observablehq--string:before{color:var(--syntax_normal)}.observablehq--collapsed,.observablehq--inspect a{cursor:pointer}.observablehq--field{text-indent:-1em;margin-left:1em}.observablehq--empty{color:var(--syntax_comment)}.observablehq--blue,.observablehq--keyword{color:#3182bd}.observablehq--forbidden,.observablehq--pink{color:#e377c2}.observablehq--orange{color:#e6550d}.observablehq--boolean,.observablehq--null,.observablehq--undefined{color:var(--syntax_atom)}.observablehq--bigint,.observablehq--date,.observablehq--green,.observablehq--number,.observablehq--regexp,.observablehq--symbol{color:var(--syntax_number)}.observablehq--index,.observablehq--key{color:var(--syntax_key)}.observablehq--prototype-key{color:#aaa}.observablehq--empty{font-style:oblique}.observablehq--purple,.observablehq--string{color:var(--syntax_string)}.observablehq--error,.observablehq--red{color:#e7040f}.observablehq--inspect{font:var(--mono_fonts);overflow-x:auto;display:block;white-space:pre}.observablehq--error .observablehq--inspect{word-break:break-all;white-space:pre-wrap}
--------------------------------------------------------------------------------
/simple-generator/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | Observable Example: Simple Generator
4 |
18 |
This example demonstrates overriding the Observable standard library. Note that straight quotes in the original have been converted to typographic (curly) quotes.
27 |
28 |
57 |
--------------------------------------------------------------------------------
/react-create-react-app/README.md:
--------------------------------------------------------------------------------
1 | # Observable Example: create-react-app
2 |
3 | To run this example, you must clone this repository and install it. From the terminal:
4 |
5 | ```
6 | git clone https://github.com/observablehq/examples.git
7 | cd examples/react-create-react-app
8 | yarn
9 | yarn start
10 | ```
11 |
12 | Then go to http://localhost:3000 to view the live app.
13 |
14 | This repo was created using `create-react-app`:
15 |
16 | ```
17 | yarn create react-app react-zoomable-sunburst
18 | ```
19 |
20 | To install the Observable runtime:
21 |
22 | ```
23 | yarn add @observablehq/runtime
24 | ```
25 |
26 | Then, go to the [D3 Zoomable Sunburst](https://observablehq.com/@d3/zoomable-sunburst) and click **Download code** in the notebook menu. Or, download this link:
27 |
28 | https://api.observablehq.com/@d3/zoomable-sunburst.tgz?v=3
29 |
30 | Copy the JavaScript files into src/@d3/zoomable-sunburst.
31 |
32 | Edit the file attachments map to use an absolute path:
33 |
34 | ```js
35 | const fileAttachments = new Map([["flare-2.json","/flare-2.json"]]);
36 | ```
37 |
38 | This step is only necessary because create-react-app does not support the standard [import.meta syntax](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import.meta), and while there is a [@babel/plugin-syntax-import-meta](https://www.npmjs.com/package/@babel/plugin-syntax-import-meta), create-react-app does not allow you to add custom Babel plugins without ejecting.
39 |
40 | Lastly, to instantiate the notebook, see App.js:
41 |
42 | ```js
43 | import {Runtime, Inspector} from '@observablehq/runtime';
44 | import React, {useEffect, useRef} from 'react';
45 | import notebook from './@d3/zoomable-sunburst';
46 | import './App.css';
47 |
48 | export default function App() {
49 | const ref = useRef();
50 |
51 | useEffect(() => {
52 | const runtime = new Runtime();
53 | runtime.module(notebook, (name) => {
54 | if (name === 'chart') {
55 | return new Inspector(ref.current);
56 | }
57 | });
58 | return () => runtime.dispose();
59 | }, []);
60 |
61 | return (
62 | <>
63 |
This example demonstrates responsive sizing where the embedded chart is 50% the width of the page. Try resizing the window to see the chart resize automatically.
22 |
23 |
61 |
--------------------------------------------------------------------------------
/react-dataflow/README.md:
--------------------------------------------------------------------------------
1 | # Observable Example: React + Dataflow (Static Import)
2 |
3 | See it live: https://observablehq.github.io/examples/react-dataflow/
4 |
5 | This modifies the React code generated by the Embed tool to facilitate dataflow between the notebook and the React app. The `height` state of `` (React) is passed down to `` (Observable), where it is passed into the Runtime’s `module.redefine` method. And a `setSelection` function is passed into the scatterplot component, which it calls in a custom [Observer](https://github.com/observablehq/runtime#observers) when the `selection` variable is fulfilled.
6 |
7 | This version imports the notebook statically, so that all the code running is checked in and bundled into your application. Contrast with [dynamic import](https://github.com/observablehq/examples/tree/main/react-dataflow-dynamic/).
8 |
9 | ```js
10 | import notebook from "@d3/brushable-scatterplot";
11 | ```
12 |
13 | The component is in BrushableScatterplot.js:
14 |
15 | ```jsx
16 | function BrushableScatterplot({height, setSelection}) {
17 | const ref = useRef();
18 | const [module, setModule] = useState();
19 |
20 | useEffect(() => {
21 | const runtime = new Runtime();
22 | const main = runtime.module(notebook, name => {
23 | // Embed the chart.
24 | if (name === "viewof selection") {
25 | return new Inspector(ref.current);
26 | }
27 | // Propagate selection state from Observable to React.
28 | if (name === "selection") {
29 | return {fulfilled: setSelection};
30 | }
31 | });
32 | setModule(main);
33 | return () => {
34 | setModule(undefined);
35 | runtime.dispose();
36 | };
37 | }, []);
38 |
39 | // Propagate height state from React to Observable.
40 | useEffect(() => {
41 | if (module !== undefined) {
42 | module.redefine("height", height);
43 | }
44 | }, [height, module]);
45 |
46 | return (
47 |
51 | );
52 | }
53 | ```
54 |
55 | This Rollup configuration supports file attachments, whereas many React environments (like the one you get from `create-react-app`) stumble on file attachments’ use of import.meta.
56 |
57 | See also the simpler [React + FileAttachment example](../react-file-attachment/).
58 |
--------------------------------------------------------------------------------
/custom-fluid-width-and-height/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
17 |
This example demonstrates responsive sizing: the embedded chart is 50% the width and height of the window. Try resizing the window to see the chart resize.
25 |
26 |
68 |
--------------------------------------------------------------------------------
/react-dataflow-dynamic/README.md:
--------------------------------------------------------------------------------
1 | # Observable Example: React + Dataflow (Dynamic Import)
2 |
3 | See it live: https://observablehq.github.io/examples/react-dataflow-dynamic/
4 |
5 | This modifies the React code generated by the Embed tool to facilitate dataflow between the notebook and the React app. The `height` state of `` (React) is passed down to `` (Observable), where it is passed into the Runtime’s `module.redefine` method. And a `setSelection` function is passed into the scatterplot component, which it calls in a custom [Observer](https://github.com/observablehq/runtime#observers) when the `selection` variable is fulfilled.
6 |
7 | This version imports the notebook dynamically, so that you’ll always see the latest published version of the notebook when you load the page, instead of having to deploy a new version of your application. This also sidesteps the issues some bundlers have with Observable file attachments. Contrast with [static import](https://github.com/observablehq/examples/tree/main/react-dataflow/).
8 |
9 | ```js
10 | const {default: notebook} = await import("https://api.observablehq.com/@d3/brushable-scatterplot.js?v=3");
11 | ```
12 |
13 | The component is in BrushableScatterplot.js:
14 |
15 | ```jsx
16 | function BrushableScatterplot({height, setSelection}) {
17 | const ref = useRef();
18 | const [module, setModule] = useState();
19 |
20 | useEffect(() => {
21 | async function load() {
22 | // Dynamically load the latest published version of the notebook,
23 | // https://observablehq.com/@d3/brushable-scatterplot.
24 | const {default: notebook} = await import("https://api.observablehq.com/@d3/brushable-scatterplot.js?v=3");
25 |
26 | const runtime = new Runtime();
27 | const main = runtime.module(notebook, name => {
28 | // Embed the chart.
29 | if (name === "viewof selection") {
30 | return new Inspector(ref.current);
31 | }
32 | // Propagate selection state from Observable to React.
33 | if (name === "selection") {
34 | return {fulfilled: setSelection};
35 | }
36 | });
37 | setModule(main);
38 | return runtime;
39 | }
40 | const promise = load();
41 | return () => {
42 | promise.then((runtime) => {
43 | setModule(undefined);
44 | runtime.dispose();
45 | });
46 | };
47 | }, []);
48 |
49 | // Propagate height state from React to Observable.
50 | useEffect(() => {
51 | if (module !== undefined) {
52 | module.redefine("height", height);
53 | }
54 | }, [height, module]);
55 |
56 | return (
57 |
83 |
105 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Observable Examples
2 |
3 | This repository contains examples of [embedding Observable notebooks](https://observablehq.com/@observablehq/introduction-to-embedding) in a variety of contexts.
4 |
5 | | Example | Description |
6 | |---|---|
7 | | ([demo](https://observablehq.github.io/examples/simple-notebook)) [**simple-notebook**](https://github.com/observablehq/examples/tree/main/simple-notebook/) | Embed an entire notebook into the page |
8 | | ([demo](https://observablehq.github.io/examples/simple-constant)) [**simple-constant**](https://github.com/observablehq/examples/tree/main/simple-constant/) | Read the value of a cell (that never changes) |
9 | | ([demo](https://observablehq.github.io/examples/simple-generator)) [**simple-generator**](https://github.com/observablehq/examples/tree/main/simple-generator/) | Read the value of a cell (that may change over time) |
10 | | ([demo](https://observablehq.github.io/examples/custom-data)) [**custom-data**](https://github.com/observablehq/examples/tree/main/custom-data/) | Pass your data into a chart |
11 | | ([demo](https://observablehq.github.io/examples/custom-live-data)) [**custom-live-data**](https://github.com/observablehq/examples/tree/main/custom-live-data/) | Pass live streaming data into a chart |
12 | | ([demo](https://observablehq.github.io/examples/custom-fixed-width)) [**custom-fixed-width**](https://github.com/observablehq/examples/tree/main/custom-fixed-width/) | Set the width of a chart to a fixed value |
13 | | ([demo](https://observablehq.github.io/examples/custom-fluid-width)) [**custom-fluid-width**](https://github.com/observablehq/examples/tree/main/custom-fluid-width/) | Resize a chart when the window is resized |
14 | | ([demo](https://observablehq.github.io/examples/custom-fluid-width-and-height)) [**custom-fluid-width-and-height**](https://github.com/observablehq/examples/tree/main/custom-fluid-width-and-height/) | Resize a chart when its container is resized |
15 | | ([demo](https://observablehq.github.io/examples/custom-library)) [**custom-library**](https://github.com/observablehq/examples/tree/main/custom-library/) | Override the Observable Standard Library |
16 | | ([demo](https://observablehq.github.io/examples/standalone)) [**standalone**](https://github.com/observablehq/examples/tree/main/standalone/) | Self-host an Observable notebook with no external dependencies |
17 | | ([demo](https://observablehq.github.io/examples/versioning)) [**versioning**](https://github.com/observablehq/examples/tree/main/versioning/) | Embed a specific version of a notebook |
18 | | ([demo](https://observablehq.github.io/examples/iframe-resize)) [**iframe-resize**](https://github.com/observablehq/examples/tree/main/iframe-resize/) | Implementing Embedly’s protocol for Iframes that resize to match their contents |
19 | | ([demo](https://observablehq.github.io/examples/react-create-react-app)) [**react-create-react-app**](https://github.com/observablehq/examples/tree/main/react-create-react-app/) | Embed an Observable notebook in a create-react-app application |
20 | | ([demo](https://observablehq.github.io/examples/react-file-attachment)) [**react-file-attachment**](https://github.com/observablehq/examples/tree/main/react-file-attachment/) | Wrap a notebook (with a file attachment) in a React component |
21 | | ([demo](https://observablehq.github.io/examples/react-dataflow)) [**react-dataflow**](https://github.com/observablehq/examples/tree/main/react-dataflow/) | Pass data between a React app and an Observable notebook |
22 | | ([demo](https://observablehq.github.io/examples/react-dataflow-dynamic)) [**react-dataflow-dynamic**](https://github.com/observablehq/examples/tree/main/react-dataflow-dynamic/) | Uses dynamic imports so republishing notebook is immediately reflected |
23 | | ([demo](https://observablehq.github.io/examples/breakout)) [**breakout**](https://github.com/observablehq/examples/tree/main/breakout/) | An extravagant way to demonstrate mutable state |
24 |
--------------------------------------------------------------------------------
/standalone/README.md:
--------------------------------------------------------------------------------
1 | # Observable Example: Standalone
2 |
3 | See it live: https://observablehq.github.io/examples/standalone/
4 |
5 | This example demonstrates how to [download](https://observablehq.com/@observablehq/downloading-and-embedding-notebooks) and run an Observable notebook with zero external dependencies in vanilla JavaScript! We also demonstrate substituting a local dataset and overriding the behavior of the notebook at runtime. You can apply the techniques here to any Observable notebook. (License-permitting; the D3 notebooks are ISC licensed.)
6 |
7 | ## How-To
8 |
9 | Go to the desired notebook and click **Download code** in the notebook menu (the three horizontal dots in the top-right corner of the screen). Here we’ll use [D3’s Bar Chart Race](https://observablehq.com/@d3/bar-chart-race). Alternatively, you can download directly from the command line, which is nice for automation:
10 |
11 | ```
12 | curl -o bar-chart-race.tgz 'https://api.observablehq.com/@d3/bar-chart-race.tgz?v=3'
13 | ```
14 |
15 | Open and expand the archive into a new folder, `bar-chart-race`.
16 |
17 | ```
18 | mkdir bar-chart-race
19 | tar -C bar-chart-race -xvzf bar-chart-race.tgz
20 | ```
21 |
22 | If the notebook requires external libraries, such as [D3](https://d3js.org), you can download local copies of the libraries to avoid loading them from an external CDN (such as jsDelivr) at runtime. D3’s Bar Chart Race uses D3, so we download the latest compatible version from npm:
23 |
24 | ```
25 | curl -o d3.js 'https://cdn.jsdelivr.net/npm/d3@6'
26 | ```
27 |
28 | All Observable notebooks require the open-source [Observable runtime](https://github.com/observablehq/runtime), which powers [Observable dataflow](https://observablehq.com/@observablehq/how-observable-runs). And don’t worry — the runtime is small, about 10KB gzipped. The runtime comes bundled with the notebook download, but if you prefer, you can download it yourself from npm.
29 |
30 | ```
31 | curl -o runtime.js 'https://cdn.jsdelivr.net/npm/@observablehq/runtime@4/dist/runtime.js'
32 | ```
33 |
34 | Some notebooks, including D3’s Bar Chart Race, use [file attachments](https://observablehq.com/@observablehq/file-attachments). Attached files are automatically included in the notebook download, too, and thus do not require external dependencies. (Note that file attachments use [import.meta](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import.meta) syntax which isn’t supported by some bundlers.) However, here we’ll further demonstrate replacing the notebook’s data with our own CSV file in the same structure.
35 |
36 | ```js
37 | // Redefine the “data” cell to use our CSV file (in the same format).
38 | main.redefine("data", d3.csv("./world-economies.csv", d3.autoType));
39 | ```
40 |
41 | If you’d like to load data from an API instead of a file, replace the d3.csv call above with a [fetch](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API). Just make sure that the promised value conforms to the structure that the notebook expects: here, an array of objects with `{date, name, category, value}` properties, where the `date` field is a date, the `name` and `category` are strings, and the `value` is a number. Inspect the notebook’s *data* cell if you’re unsure what format is expected. (If you already have the data loaded, you can also pass an array directly, rather than needing a promise.)
42 |
43 | Lastly, we need an HTML file to host the chart. The full source is in [index.html](./index.html). Here’s the relevant bit:
44 |
45 | ```html
46 |
47 |
48 |
49 |
80 | ```
81 |
82 | To see it live:
83 |
84 | https://observablehq.github.io/notebook-download-example/
85 |
86 | ## Further Resources
87 |
88 | If you’re doing this in the context of a larger web application, you can call [*runtime*.dispose](https://github.com/observablehq/runtime/blob/master/README.md#runtime_dispose) to clean up if you no longer want to show the chart. For more advanced techniques, including two-way coordination between vanilla JavaScript and Observable JavaScript, see our tutorial on [embedding Observable in React](https://next.observablehq.com/@observablehq/how-to-embed-a-notebook-in-a-react-app).
89 |
90 | If you’d prefer live embeds to standalone code, see our [embed examples](https://github.com/observablehq/examples).
91 |
--------------------------------------------------------------------------------
/react-create-react-app/src/serviceWorker.js:
--------------------------------------------------------------------------------
1 | // This optional code is used to register a service worker.
2 | // register() is not called by default.
3 |
4 | // This lets the app load faster on subsequent visits in production, and gives
5 | // it offline capabilities. However, it also means that developers (and users)
6 | // will only see deployed updates on subsequent visits to a page, after all the
7 | // existing tabs open on the page have been closed, since previously cached
8 | // resources are updated in the background.
9 |
10 | // To learn more about the benefits of this model and instructions on how to
11 | // opt-in, read https://bit.ly/CRA-PWA
12 |
13 | const isLocalhost = Boolean(
14 | window.location.hostname === 'localhost' ||
15 | // [::1] is the IPv6 localhost address.
16 | window.location.hostname === '[::1]' ||
17 | // 127.0.0.0/8 are considered localhost for IPv4.
18 | window.location.hostname.match(
19 | /^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/
20 | )
21 | );
22 |
23 | export function register(config) {
24 | if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) {
25 | // The URL constructor is available in all browsers that support SW.
26 | const publicUrl = new URL(process.env.PUBLIC_URL, window.location.href);
27 | if (publicUrl.origin !== window.location.origin) {
28 | // Our service worker won't work if PUBLIC_URL is on a different origin
29 | // from what our page is served on. This might happen if a CDN is used to
30 | // serve assets; see https://github.com/facebook/create-react-app/issues/2374
31 | return;
32 | }
33 |
34 | window.addEventListener('load', () => {
35 | const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`;
36 |
37 | if (isLocalhost) {
38 | // This is running on localhost. Let's check if a service worker still exists or not.
39 | checkValidServiceWorker(swUrl, config);
40 |
41 | // Add some additional logging to localhost, pointing developers to the
42 | // service worker/PWA documentation.
43 | navigator.serviceWorker.ready.then(() => {
44 | console.log(
45 | 'This web app is being served cache-first by a service ' +
46 | 'worker. To learn more, visit https://bit.ly/CRA-PWA'
47 | );
48 | });
49 | } else {
50 | // Is not localhost. Just register service worker
51 | registerValidSW(swUrl, config);
52 | }
53 | });
54 | }
55 | }
56 |
57 | function registerValidSW(swUrl, config) {
58 | navigator.serviceWorker
59 | .register(swUrl)
60 | .then(registration => {
61 | registration.onupdatefound = () => {
62 | const installingWorker = registration.installing;
63 | if (installingWorker == null) {
64 | return;
65 | }
66 | installingWorker.onstatechange = () => {
67 | if (installingWorker.state === 'installed') {
68 | if (navigator.serviceWorker.controller) {
69 | // At this point, the updated precached content has been fetched,
70 | // but the previous service worker will still serve the older
71 | // content until all client tabs are closed.
72 | console.log(
73 | 'New content is available and will be used when all ' +
74 | 'tabs for this page are closed. See https://bit.ly/CRA-PWA.'
75 | );
76 |
77 | // Execute callback
78 | if (config && config.onUpdate) {
79 | config.onUpdate(registration);
80 | }
81 | } else {
82 | // At this point, everything has been precached.
83 | // It's the perfect time to display a
84 | // "Content is cached for offline use." message.
85 | console.log('Content is cached for offline use.');
86 |
87 | // Execute callback
88 | if (config && config.onSuccess) {
89 | config.onSuccess(registration);
90 | }
91 | }
92 | }
93 | };
94 | };
95 | })
96 | .catch(error => {
97 | console.error('Error during service worker registration:', error);
98 | });
99 | }
100 |
101 | function checkValidServiceWorker(swUrl, config) {
102 | // Check if the service worker can be found. If it can't reload the page.
103 | fetch(swUrl, {
104 | headers: { 'Service-Worker': 'script' },
105 | })
106 | .then(response => {
107 | // Ensure service worker exists, and that we really are getting a JS file.
108 | const contentType = response.headers.get('content-type');
109 | if (
110 | response.status === 404 ||
111 | (contentType != null && contentType.indexOf('javascript') === -1)
112 | ) {
113 | // No service worker found. Probably a different app. Reload the page.
114 | navigator.serviceWorker.ready.then(registration => {
115 | registration.unregister().then(() => {
116 | window.location.reload();
117 | });
118 | });
119 | } else {
120 | // Service worker found. Proceed as normal.
121 | registerValidSW(swUrl, config);
122 | }
123 | })
124 | .catch(() => {
125 | console.log(
126 | 'No internet connection found. App is running in offline mode.'
127 | );
128 | });
129 | }
130 |
131 | export function unregister() {
132 | if ('serviceWorker' in navigator) {
133 | navigator.serviceWorker.ready
134 | .then(registration => {
135 | registration.unregister();
136 | })
137 | .catch(error => {
138 | console.error(error.message);
139 | });
140 | }
141 | }
142 |
--------------------------------------------------------------------------------
/react-create-react-app/src/@d3/zoomable-sunburst/86ddbc29bd33f9d6@351.js:
--------------------------------------------------------------------------------
1 | // https://observablehq.com/@d3/zoomable-sunburst@351
2 | export default function define(runtime, observer) {
3 | const main = runtime.module();
4 | const fileAttachments = new Map([["flare-2.json","/flare.json"]]);
5 | main.builtin("FileAttachment", runtime.fileAttachments(name => fileAttachments.get(name)));
6 | main.variable(observer()).define(["md"], function(md){return(
7 | md`# Zoomable Sunburst
8 |
9 | This variant of a [sunburst diagram](/@d3/sunburst) shows only two layers of the hierarchy at a time. Click a node to zoom in, or the center to zoom out. Compare to an [icicle](/@d3/zoomable-icicle).`
10 | )});
11 | main.variable(observer("chart")).define("chart", ["partition","data","d3","width","color","arc","format","radius"], function(partition,data,d3,width,color,arc,format,radius)
12 | {
13 | const root = partition(data);
14 |
15 | root.each(d => d.current = d);
16 |
17 | const svg = d3.create("svg")
18 | .attr("viewBox", [0, 0, width, width])
19 | .style("font", "10px sans-serif");
20 |
21 | const g = svg.append("g")
22 | .attr("transform", `translate(${width / 2},${width / 2})`);
23 |
24 | const path = g.append("g")
25 | .selectAll("path")
26 | .data(root.descendants().slice(1))
27 | .join("path")
28 | .attr("fill", d => { while (d.depth > 1) d = d.parent; return color(d.data.name); })
29 | .attr("fill-opacity", d => arcVisible(d.current) ? (d.children ? 0.6 : 0.4) : 0)
30 | .attr("d", d => arc(d.current));
31 |
32 | path.filter(d => d.children)
33 | .style("cursor", "pointer")
34 | .on("click", clicked);
35 |
36 | path.append("title")
37 | .text(d => `${d.ancestors().map(d => d.data.name).reverse().join("/")}\n${format(d.value)}`);
38 |
39 | const label = g.append("g")
40 | .attr("pointer-events", "none")
41 | .attr("text-anchor", "middle")
42 | .style("user-select", "none")
43 | .selectAll("text")
44 | .data(root.descendants().slice(1))
45 | .join("text")
46 | .attr("dy", "0.35em")
47 | .attr("fill-opacity", d => +labelVisible(d.current))
48 | .attr("transform", d => labelTransform(d.current))
49 | .text(d => d.data.name);
50 |
51 | const parent = g.append("circle")
52 | .datum(root)
53 | .attr("r", radius)
54 | .attr("fill", "none")
55 | .attr("pointer-events", "all")
56 | .on("click", clicked);
57 |
58 | function clicked(event, p) {
59 | parent.datum(p.parent || root);
60 |
61 | root.each(d => d.target = {
62 | x0: Math.max(0, Math.min(1, (d.x0 - p.x0) / (p.x1 - p.x0))) * 2 * Math.PI,
63 | x1: Math.max(0, Math.min(1, (d.x1 - p.x0) / (p.x1 - p.x0))) * 2 * Math.PI,
64 | y0: Math.max(0, d.y0 - p.depth),
65 | y1: Math.max(0, d.y1 - p.depth)
66 | });
67 |
68 | const t = g.transition().duration(750);
69 |
70 | // Transition the data on all arcs, even the ones that aren’t visible,
71 | // so that if this transition is interrupted, entering arcs will start
72 | // the next transition from the desired position.
73 | path.transition(t)
74 | .tween("data", d => {
75 | const i = d3.interpolate(d.current, d.target);
76 | return t => d.current = i(t);
77 | })
78 | .filter(function(d) {
79 | return +this.getAttribute("fill-opacity") || arcVisible(d.target);
80 | })
81 | .attr("fill-opacity", d => arcVisible(d.target) ? (d.children ? 0.6 : 0.4) : 0)
82 | .attrTween("d", d => () => arc(d.current));
83 |
84 | label.filter(function(d) {
85 | return +this.getAttribute("fill-opacity") || labelVisible(d.target);
86 | }).transition(t)
87 | .attr("fill-opacity", d => +labelVisible(d.target))
88 | .attrTween("transform", d => () => labelTransform(d.current));
89 | }
90 |
91 | function arcVisible(d) {
92 | return d.y1 <= 3 && d.y0 >= 1 && d.x1 > d.x0;
93 | }
94 |
95 | function labelVisible(d) {
96 | return d.y1 <= 3 && d.y0 >= 1 && (d.y1 - d.y0) * (d.x1 - d.x0) > 0.03;
97 | }
98 |
99 | function labelTransform(d) {
100 | const x = (d.x0 + d.x1) / 2 * 180 / Math.PI;
101 | const y = (d.y0 + d.y1) / 2 * radius;
102 | return `rotate(${x - 90}) translate(${y},0) rotate(${x < 180 ? 0 : 180})`;
103 | }
104 |
105 | return svg.node();
106 | }
107 | );
108 | main.variable(observer("data")).define("data", ["FileAttachment"], function(FileAttachment){return(
109 | FileAttachment("flare-2.json").json()
110 | )});
111 | main.variable(observer("partition")).define("partition", ["d3"], function(d3){return(
112 | data => {
113 | const root = d3.hierarchy(data)
114 | .sum(d => d.value)
115 | .sort((a, b) => b.value - a.value);
116 | return d3.partition()
117 | .size([2 * Math.PI, root.height + 1])
118 | (root);
119 | }
120 | )});
121 | main.variable(observer("color")).define("color", ["d3","data"], function(d3,data){return(
122 | d3.scaleOrdinal(d3.quantize(d3.interpolateRainbow, data.children.length + 1))
123 | )});
124 | main.variable(observer("format")).define("format", ["d3"], function(d3){return(
125 | d3.format(",d")
126 | )});
127 | main.variable(observer("width")).define("width", function(){return(
128 | 932
129 | )});
130 | main.variable(observer("radius")).define("radius", ["width"], function(width){return(
131 | width / 6
132 | )});
133 | main.variable(observer("arc")).define("arc", ["d3","radius"], function(d3,radius){return(
134 | d3.arc()
135 | .startAngle(d => d.x0)
136 | .endAngle(d => d.x1)
137 | .padAngle(d => Math.min((d.x1 - d.x0) / 2, 0.005))
138 | .padRadius(radius * 1.5)
139 | .innerRadius(d => d.y0 * radius)
140 | .outerRadius(d => Math.max(d.y0 * radius, d.y1 * radius - 1))
141 | )});
142 | main.variable(observer("d3")).define("d3", ["require"], function(require){return(
143 | require("d3@6.0.0-rc.1")
144 | )});
145 | return main;
146 | }
147 |
--------------------------------------------------------------------------------
/standalone/bar-chart-race/3ff9fa2c6593d814@3048.js:
--------------------------------------------------------------------------------
1 | // https://observablehq.com/@d3/bar-chart-race@3048
2 | export default function define(runtime, observer) {
3 | const main = runtime.module();
4 | const fileAttachments = new Map([["category-brands.csv",new URL("./files/aec3792837253d4c6168f9bbecdf495140a5f9bb1cdb12c7c8113cec26332634a71ad29b446a1e8236e0a45732ea5d0b4e86d9d1568ff5791412f093ec06f4f1",import.meta.url)]]);
5 | main.builtin("FileAttachment", runtime.fileAttachments(name => fileAttachments.get(name)));
6 | main.variable(observer()).define(["md"], function(md){return(
7 | md`# Bar Chart Race
8 |
9 | This chart animates the value (in $M) of the top global brands from 2000 to 2019. Color indicates sector. See [the explainer](/d/e9e3929cf7c50b45) for more. Data: [Interbrand](https://www.interbrand.com/best-brands/)`
10 | )});
11 | main.variable(observer("data")).define("data", ["FileAttachment"], function(FileAttachment){return(
12 | FileAttachment("category-brands.csv").csv({typed: true})
13 | )});
14 | main.variable(observer("viewof replay")).define("viewof replay", ["html"], function(html){return(
15 | html`