Skip to content

Commit

Permalink
Merge branch 'main' into fil/render-api-docs
Browse files Browse the repository at this point in the history
  • Loading branch information
Fil authored Sep 30, 2024
2 parents a1c43ca + 12ecfa7 commit d076ff2
Show file tree
Hide file tree
Showing 254 changed files with 21,973 additions and 3,397 deletions.
5 changes: 5 additions & 0 deletions .github/ISSUE_TEMPLATE/bug.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
name: Bug
about: Something isn’t working
labels: "bug"
---
5 changes: 5 additions & 0 deletions .github/ISSUE_TEMPLATE/enhancement.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
name: Enhancement
about: New feature or request
labels: "enhancement"
---
2 changes: 1 addition & 1 deletion CHANGELOG-2023.md
Original file line number Diff line number Diff line change
Expand Up @@ -233,7 +233,7 @@ Plot.bollingerY(aapl, {x: "Date", y: "Close", n: 20, k: 2}).plot()

The [arrow mark](https://observablehq.com/plot/marks/arrow) supports a new **sweep** option to control the bend orientation. Below, we set this option to *-y* to draw arrows bulging right, independent of the relative vertical positions of its source and target.

[<img src="./img/arc-diagram.png" width="521" alt="Detail of an arc diagram connecting characters in Les Misérables that appear in the same chapters.">](https://observablehq.com/@observablehq/plot-arc-diagram?intent=fork)
[<img src="./img/arc-diagram.png" width="521" alt="Detail of an arc diagram connecting characters in Les Misérables that appear in the same chapters.">](https://observablehq.com/@observablehq/plot-arc-diagram)

```js
Plot.plot({
Expand Down
111 changes: 111 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,117 @@

Year: **Current (2024)** · [2023](./CHANGELOG-2023.md) · [2022](./CHANGELOG-2022.md) · [2021](./CHANGELOG-2021.md)

## 0.6.16

[Released August 6, 2024.](https://github.com/observablehq/plot/releases/tag/v0.6.16)

The new [waffle mark](https://observablehq.com/plot/marks/waffle) 🧇 displays a quantity (or quantitative extent) for a given category; unlike a [bar](https://observablehq.com/plot/marks/bar), a waffle is subdivided into cells that allow easier counting, making waffles useful for reading and comparing exact quantities. Plot’s waffle mark is highly configurable: it supports stacking, positive and negative values, rounded corners, partial cells for fractional counts, automatic row or column size determination (with optional override), and more!

[<img src="./img/waffle.png" width="708" alt="a waffle chart of Olympic athletes by weight">](https://observablehq.com/plot/marks/waffle)

```js
Plot.plot({
fx: {interval: 10},
color: {legend: true},
marks: [Plot.waffleY(olympians, Plot.groupZ({y: "count"}, {fill: "sex", sort: "sex", fx: "weight", unit: 10}))]
})
```


All marks now support GeoJSON data and GeoJSON property shorthand, making it easier to work with GeoJSON. For example, below the data `counties` is a GeoJSON FeatureCollection, and `unemployment` refers to a property on each feature; the **fill** option is thus shorthand for `(d) => d.properties.unemployment`. The [geo mark](https://observablehq.com/plot/marks/geo) now also supports the **tip** option (via an implicit [centroid transform](https://observablehq.com/plot/transforms/centroid)), making it easier to use Plot’s [interactive tooltips](https://observablehq.com/plot/interactions/pointer).

[<img src="./img/geo-tip.png" width="708" alt="a choropleth map of unemployment by U.S. county">](https://observablehq.com/plot/marks/geo)

```js
Plot.plot({
projection: "albers-usa",
color: {
type: "quantile",
n: 9,
scheme: "blues",
label: "Unemployment (%)",
legend: true
},
marks: [
Plot.geo(counties, {
fill: "unemployment",
title: (d) => `${d.properties.name} ${d.properties.unemployment}%`,
tip: true
})
]
})
```

All marks now also support column name channel shorthand when using Apache Arrow tables as data, and we’ve added detection of Arrow date-type columns. (Arrow represents temporal data using BigInt rather than Date.)

```js
Plot.dot(gistemp, {x: "Date", y: "Anomaly"}).plot() // gistemp is an Arrow Table!
```

The rect-like marks ([rect](https://observablehq.com/plot/marks/rect), [bar](https://observablehq.com/plot/marks/bar), [cell](https://observablehq.com/plot/marks/cell), and [frame](https://observablehq.com/plot/marks/frame)) now support individual rounding options for each side (**rx1**, **ry1**, *etc.*) and corner (**rx1y1**, **rx2y1**, *etc.*). This allows you to round just the top side of rects. You can even use a negative corner radius on the bottom side for seamless stacking, as in the histogram of Olympic athletes below.

[<img src="./img/rect-rounded.png" width="708" alt="a histogram of Olympic athletes by weight">](https://observablehq.com/plot/marks/rect)

```js
Plot.plot({
color: {legend: true},
marks: [
Plot.rectY(olympians, Plot.binX({y: "count"}, {x: "weight", fill: "sex", ry2: 4, ry1: -4, clip: "frame"})),
Plot.ruleY([0])
]
})
```

Plot now respects the projection **domain** when determining the default plot height. Previously, the map below would use a default square aspect ratio for the *conic-conformal* projection regardless of the specified **domain**, but now the map is perfectly sized to fit North Carolina. (Plot also now chooses a smarter default plot height when the ordinal *y* scale domain is empty.)

<img src="./img/geo-nc.png" width="659" alt="an unlabeled map showing the outline and counties of North Carolina">

```js
Plot.plot({
projection: {.
type: "conic-conformal",
parallels: [34 + 20 / 60, 36 + 10 / 60],
rotate: [79, 0],
domain: state
},
marks: [
Plot.geo(counties, {strokeOpacity: 0.2}),
Plot.geo(state)
]
})
```

The [marker options](https://observablehq.com/plot/features/markers) now render as intended on marks with varying aesthetics, such as the spiraling arrows of varying thickness and color below.

<img src="./img/group-marker.png" width="659" alt="several spiraling lines emanate from the center of the image, with rainbow color and increasing thickness, each capped with a pointed arrow at the end">

```js
Plot.plot({
inset: 40,
axis: null,
marks: [
Plot.line(d3.range(400), {
x: (i) => i * Math.sin(i / 100 + ((i % 5) * 2 * Math.PI) / 5),
y: (i) => i * Math.cos(i / 100 + ((i % 5) * 2 * Math.PI) / 5),
z: (i) => i % 5,
stroke: (i) => -i,
strokeWidth: (i) => i ** 1.1 / 100,
markerEnd: "arrow"
})
]
})
```

This release includes a few more new features, bug fixes, and improvements:

The new **className** [mark option](https://observablehq.com/plot/features/marks#mark-options) specifies an optional `class` attribute for rendered marks, allowing styling of marks via external stylesheets or easier selection via JavaScript; thanks, @RLesser! Plot now reuses `clipPath` elements, when possible, when the **clip** mark option is set to *frame* or *projection*.

The [difference mark](https://observablehq.com/plot/marks/difference) now supports a horizontal orientation via [differenceX](https://observablehq.com/plot/marks/difference#differenceX), and the [shift transform](https://observablehq.com/plot/transforms/shift) now likewise supports [shiftY](https://observablehq.com/plot/transforms/shift#shiftY). The [Voronoi mark](https://observablehq.com/plot/marks/delaunay) is now compatible with the pointer transform: only the pointed Voronoi cell is rendered; the Voronoi mark now also renders as intended with non-exclusive facets (as when using the *exclude* facet mode). The [tip mark](https://observablehq.com/plot/marks/tip) no longer displays channels containing literal color values by default.

## 0.6.15

[Released June 11, 2024.](https://github.com/observablehq/plot/releases/tag/v0.6.15)

## 0.6.14

[Released March 12, 2024.](https://github.com/observablehq/plot/releases/tag/v0.6.14)
Expand Down
8 changes: 7 additions & 1 deletion docs/.vitepress/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import plot from "./markdown-it-plot.js";
export default defineConfig({
title: "Observable Plot",
description: "The JavaScript library for exploratory data visualization",
appearance: "force-auto",
base: "/plot/",
cleanUrls: true,
vite: {
Expand All @@ -24,6 +25,9 @@ export default defineConfig({
}
},
head: [
["link", {rel: "preconnect", href: "https://fonts.gstatic.com", crossorigin: ""}],
["link", {rel: "preload", as: "style", href: "https://fonts.googleapis.com/css2?family=Inter:ital,opsz,wght@0,14..32,100..900;1,14..32,100..900&family=Spline+Sans+Mono:ital,wght@0,300..700;1,300..700&display=swap"}],
["link", {rel: "stylesheet", href: "https://fonts.googleapis.com/css2?family=Inter:ital,opsz,wght@0,14..32,100..900;1,14..32,100..900&family=Spline+Sans+Mono:ital,wght@0,300..700;1,300..700&display=swap"}],
["link", {rel: "apple-touch-icon", href: "https://static.observablehq.com/favicon-512.0667824687f99c942a02e06e2db1a060911da0bf3606671676a255b1cf97b4fe.png"}],
["link", {rel: "icon", type: "image/png", href: "https://static.observablehq.com/favicon-512.0667824687f99c942a02e06e2db1a060911da0bf3606671676a255b1cf97b4fe.png", sizes: "512x512"}],
["script", {async: "", src: "https://www.googletagmanager.com/gtag/js?id=G-9B88TP6PKQ"}],
Expand Down Expand Up @@ -68,6 +72,7 @@ export default defineConfig({
{text: "Legends", link: "/features/legends"},
{text: "Curves", link: "/features/curves"},
{text: "Formats", link: "/features/formats"},
{text: "Intervals", link: "/features/intervals"},
{text: "Markers", link: "/features/markers"},
{text: "Shorthand", link: "/features/shorthand"},
{text: "Accessibility", link: "/features/accessibility"}
Expand Down Expand Up @@ -105,7 +110,8 @@ export default defineConfig({
{text: "Tick", link: "/marks/tick"},
{text: "Tip", link: "/marks/tip"},
{text: "Tree", link: "/marks/tree"},
{text: "Vector", link: "/marks/vector"}
{text: "Vector", link: "/marks/vector"},
{text: "Waffle", link: "/marks/waffle"}
]
},
{
Expand Down
2 changes: 1 addition & 1 deletion docs/.vitepress/markdown-it-plot.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ export default function plot(md) {
directives.includes("hidden")
? `<div style="display: none;">\n`
: href
? `<a class="plot-fork no-icon" href="${md.utils.escapeHtml(href)}?intent=fork" target="_blank" title="Open on Observable">Fork</a>`
? `<a class="plot-fork no-icon" href="${md.utils.escapeHtml(href)}" target="_blank" title="Open on Observable">Fork</a>`
: ""
}`;
if (/^Plot\.plot\(/.test(content)) {
Expand Down
1 change: 0 additions & 1 deletion docs/.vitepress/theme/CustomFooter.vue
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@
<li class="mb2"><a target="_blank" href="https://observablehq.com/plot">Plot</a></li>
<li class="mb2"><a target="_blank" href="https://observablehq.com/data-integrations">Integrations</a></li>
<li class="mb2"><a target="_blank" href="https://observablehq.com/pricing">Pricing</a></li>
<li class="mb2"><a target="_blank" href="https://observablehq.com/enterprise">Enterprise</a></li>
</ul>
</div>
</div>
Expand Down
2 changes: 1 addition & 1 deletion docs/.vitepress/theme/ExamplesGrid.vue
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ onUnmounted(() => {
<template>
<div :class="$style.examples" ref="container" :style="`transform: translate(${60 - x * 10}vw, 33%);`">
<div v-for="(d, i) in sample" :style="`--delay: ${((i % xn) / xn + (d3.randomLcg(1 / i)()) - 0.4) * 1}s;`">
<a :href="`https://observablehq.com/${d.path}${d.path.includes('?') ? '&' : '?'}intent=fork`" :title="[d.title, d.author].filter(Boolean).join('\n')" target="_blank" :style="`--x: ${(i % xn) - xn / 2 + (Math.floor(i / xn) % 2) * 0.5}; --y: ${Math.floor(i / xn) - yn / 2};`">
<a :href="`https://observablehq.com/${d.path}`" :title="[d.title, d.author].filter(Boolean).join('\n')" target="_blank" :style="`--x: ${(i % xn) - xn / 2 + (Math.floor(i / xn) % 2) * 0.5}; --y: ${Math.floor(i / xn) - yn / 2};`">
<img :src="`https://static.observableusercontent.com/thumbnail/${d.thumbnail}.jpg`" width="640" height="400" />
</a>
</div>
Expand Down
2 changes: 1 addition & 1 deletion docs/.vitepress/theme/ObservablePromo.vue
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ h1 {
opacity: 0.7;
}
a.button {
.promo a.button {
display: inline-block;
border: 1px solid transparent;
text-align: center;
Expand Down
54 changes: 36 additions & 18 deletions docs/.vitepress/theme/custom.css
Original file line number Diff line number Diff line change
@@ -1,27 +1,45 @@
:root {
--vp-c-purple-1: #7135be;
--vp-c-purple-2: #7f42cd;
--vp-c-purple-3: #9555e2;
--vp-c-purple-soft: rgba(155, 91, 233, 0.14);
--vp-c-red: #f43f5e;
--vp-c-green: #10b981;
--vp-c-blue: #0092ff;
--vp-c-purple: #a463f2;
--theme-phosphate: #148576;
--theme-phosphate-2: #1da492;
--theme-phosphate-3: #26c1ad;
--theme-phosphate-soft: #d7fbf7;
--vp-c-brand-1: var(--theme-phosphate); /* link and brand color */
--vp-c-brand-2: var(--theme-phosphate-2);
--vp-c-brand-3: var(--theme-phosphate-3);
--hero-brand-contrast: rgb(243, 139, 233); /* home page alt color */
--vp-c-brand-soft: var(--theme-phosphate-soft);
--mono-heading: "Spline Sans Mono", monospace;
--vp-font-family-mono: var(--mono-heading);
--vp-font-family-base: Inter, -apple-system, BlinkMacSystemFont, "avenir next", avenir, helvetica, "helvetica neue", ubuntu, roboto, noto, "segoe ui", arial, sans-serif;
--vp-code-color: inherit;
--vp-code-font-size: 14px;
--vp-code-line-height: 1.5;
}

.dark {
--vp-c-purple-1: #db96ff;
--vp-c-purple-2: #9a5ae8;
--vp-c-purple-3: #884ad6;
--vp-c-purple-soft: rgba(155, 91, 233, 0.16);
--theme-phosphate: #37d5be;
--theme-phosphate-2: #28b39e;
--theme-phosphate-3: #1b9583;
--hero-brand-contrast: rgb(183, 41, 169);
--theme-phosphate-soft: #033a32;
--vp-c-text-1: #f5f5f5;
}

:root {
--vp-c-red: #f43f5e;
--vp-c-green: #10b981;
--vp-c-blue: #0092ff;
--vp-c-purple: #a463f2;
--vp-c-brand-1: var(--vp-c-purple-1);
--vp-c-brand-2: var(--vp-c-purple-2);
--vp-c-brand-3: var(--vp-c-purple-3);
--vp-c-brand-soft: var(--vp-c-purple-soft);
--vp-font-family-base: -apple-system, BlinkMacSystemFont, "avenir next", avenir, helvetica, "helvetica neue", ubuntu, roboto, noto, "segoe ui", arial, sans-serif;
.vp-doc h1 {
font-family: var(--mono-heading);
font-weight: 500;
}

.vp-doc p {
line-height: 1.5;
}

.vp-doc a {
color: var(--vp-c-text-1);
}

.vp-doc figcaption {
Expand Down
8 changes: 4 additions & 4 deletions docs/.vitepress/theme/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,11 @@ export default {
}
};

async function enableAnalytics(router) {
function enableAnalytics(router) {
if (typeof location === "undefined" || location.origin !== "https://observablehq.com") return;
const {pageLoad, routeChanged} = await import("https://events.observablehq.com/client.js");
let pageLoaded;
watch(router.route, () => {
let pageLoaded = false;
watch(router.route, async () => {
const {pageLoad, routeChanged} = await import("https://events.observablehq.com/client.js");
if (pageLoaded) {
routeChanged();
} else {
Expand Down
6 changes: 4 additions & 2 deletions docs/community.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ And of course, follow us on [Observable](https://observablehq.com/@observablehq?

## Getting help

We recommend asking for help on the [Observable forum](https://talk.observablehq.com/c/help/6). Or if you prefer chat, join the [Observable community Slack](https://observablehq.com/slack/join).
We recommend asking for help on [GitHub discussions](https://github.com/observablehq/plot/discussions).

We encourage you to share your work, no matter how messy, on [Observable](https://observablehq.com). Sharing live code is the easiest way to let people see what you see, and to debug your problem. Strive for a [minimal, reproducible example](https://stackoverflow.com/help/minimal-reproducible-example) — it helps people hone in on your problem more quickly.

Expand All @@ -35,7 +35,9 @@ We’d love for you to join the community! Here are some ways to participate:

* Upvote 👍 or comment on [GitHub issues](https://github.com/observablehq/plot/issues). We’d love your input on what to build next. If your desired feature isn’t already there, or if you’ve found a bug, file an issue and tell us about it.

* Answer questions or participate in discussions on the [Observable forum](https://talk.observablehq.com/) and the [Observable community Slack](https://observablehq.com/slack/join). You’ll help others, and might learn something yourself, too.
* Answer questions or participate in discussions on [GitHub](https://github.com/observablehq/plot/discussions). You’ll help others, and might learn something yourself, too.

* Join the [Observable community Slack](https://observablehq.com/slack/join) to meet others using Plot.

* Open a pull request! Read our [guide to contributing](https://github.com/observablehq/plot/blob/main/CONTRIBUTING.md).

Expand Down
6 changes: 6 additions & 0 deletions docs/components/PlotRender.js
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,12 @@ class Element {
child.parentNode = this;
return child;
}
cloneNode(deep) {
const clone = new Element(this.ownerDocument, this.tagName);
clone.attributes = {...this.attributes};
if (deep) clone.children = this.children.map((child) => child.cloneNode(deep));
return clone;
}
querySelector() {
return null;
}
Expand Down
22 changes: 14 additions & 8 deletions docs/data/api.data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,13 +43,12 @@ function getHref(name: string, path: string): string {
switch (path) {
case "features/curve":
case "features/format":
case "features/interval":
case "features/mark":
case "features/marker":
case "features/plot":
case "features/projection":
return `${path}s`;
case "features/inset":
return "features/scales";
case "features/options":
return "features/transforms";
case "marks/axis": {
Expand Down Expand Up @@ -84,8 +83,8 @@ function getInterfaceName(name: string, path: string): string {
name = name.replace(/([a-z0-9])([A-Z])/, (_, a, b) => `${a} ${b}`); // camel case conversion
name = name.toLowerCase();
if (name === "curve auto") name = "curve";
if (name === "plot facet") name = "plot";
if (name === "bollinger window") name = "bollinger map method";
else if (name === "plot facet") name = "plot";
else if (name === "bollinger window") name = "bollinger map method";
else if (path.startsWith("marks/")) name += " mark";
else if (path.startsWith("transforms/")) name += " transform";
return name;
Expand All @@ -104,10 +103,15 @@ export default {
if (Node.isInterfaceDeclaration(declaration)) {
if (isInternalInterface(name)) continue;
for (const property of declaration.getProperties()) {
const path = index.getRelativePathTo(declaration.getSourceFile());
const href = getHref(name, path);
if (property.getJsDocs().some((d) => d.getTags().some((d) => Node.isJSDocDeprecatedTag(d)))) continue;
allOptions.push({name: property.getName(), context: {name: getInterfaceName(name, path), href}});
if (name === "InsetOptions") {
allOptions.push({name: property.getName(), context: {name: "mark", href: "features/marks"}});
allOptions.push({name: property.getName(), context: {name: "scale", href: "features/scales"}});
} else {
const path = index.getRelativePathTo(declaration.getSourceFile());
const href = getHref(name, path);
allOptions.push({name: property.getName(), context: {name: getInterfaceName(name, path), href}});
}
}
} else if (Node.isFunctionDeclaration(declaration)) {
const comment = getDescription(declaration);
Expand Down Expand Up @@ -140,7 +144,9 @@ export default {
throw new Error(`anchor not found: ${href}#${name}`);
}
}
for (const {context: {href}} of allOptions) {
for (const {
context: {href}
} of allOptions) {
if (!anchors.has(`/${href}.md`)) {
throw new Error(`file not found: ${href}`);
}
Expand Down
2 changes: 1 addition & 1 deletion docs/features/facets.md
Original file line number Diff line number Diff line change
Expand Up @@ -246,7 +246,7 @@ Faceting can be explicitly enabled or disabled on a mark with the **facet** opti

When mark-level faceting is used, the default *auto* setting is equivalent to *include*: the mark will be faceted if either the **fx** or **fy** channel option (or both) is specified. The null or false option will disable faceting, while *exclude* draws the subset of the mark’s data *not* in the current facet. When a mark uses *super* faceting, it is not allowed to use position scales (*x*, *y*, *fx*, or *fy*); *super* faceting is intended for decorations, such as labels and legends.

The **facetAnchor** option<a id="facetAnchor" class="header-anchor" href="#facetAnchor" aria-label="Permalink to &quot;facetAnchor&quot;"></a> <VersionBadge version="0.6.3" /> controls the placement of the mark with respect to the facets. Based on the value, the mark will be displayed on:
The **facetAnchor** option<a id="facetAnchor" href="#facetAnchor" aria-label="Permalink to &quot;facetAnchor&quot;"></a> <VersionBadge version="0.6.3" /> controls the placement of the mark with respect to the facets. Based on the value, the mark will be displayed on:

* null - non-empty facets
* *top*, *right*, *bottom*, or *left* - the given side
Expand Down
Loading

0 comments on commit d076ff2

Please sign in to comment.