From 0be7eacfe080a34b2f4ae07d9757c38f507b609f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Rivi=C3=A8re?= Date: Thu, 23 Jun 2022 18:50:25 +0200 Subject: [PATCH] Plot.frame rounded corners closes #942 --- README.md | 2 +- src/marks/frame.js | 12 +++++++++--- test/output/frameCorners.svg | 17 +++++++++++++++++ test/plots/frame-corners.js | 5 +++++ test/plots/index.js | 1 + 5 files changed, 33 insertions(+), 4 deletions(-) create mode 100644 test/output/frameCorners.svg create mode 100644 test/plots/frame-corners.js diff --git a/README.md b/README.md index 9dc70aa690..a3278e9474 100644 --- a/README.md +++ b/README.md @@ -1500,7 +1500,7 @@ Decorations are static marks that do not represent data. Currently this includes [Source](./src/marks/frame.js) · [Examples](https://observablehq.com/@observablehq/plot-frame) · Draws a simple frame around the entire plot (or facet). -The frame mark supports the [standard mark options](#marks), but does not accept any data or support channels. The default **stroke** is currentColor, and the default **fill** is none. +The frame mark supports the [standard mark options](#marks), and the **rx** and **ry** options to set the [*x* radius](https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/rx) and [*y* radius](https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/ry) for rounded corners. It does not accept any data or support channels. The default **stroke** is currentColor, and the default **fill** is none. #### Plot.frame(*options*) diff --git a/src/marks/frame.js b/src/marks/frame.js index 348e172956..853061e1df 100644 --- a/src/marks/frame.js +++ b/src/marks/frame.js @@ -16,17 +16,21 @@ export class Frame extends Mark { insetTop = inset, insetRight = inset, insetBottom = inset, - insetLeft = inset + insetLeft = inset, + rx, + ry } = options; super(undefined, undefined, options, defaults); this.insetTop = number(insetTop); this.insetRight = number(insetRight); this.insetBottom = number(insetBottom); this.insetLeft = number(insetLeft); + this.rx = number(rx); + this.ry = number(ry); } render(index, scales, channels, dimensions) { const {marginTop, marginRight, marginBottom, marginLeft, width, height} = dimensions; - const {insetTop, insetRight, insetBottom, insetLeft} = this; + const {insetTop, insetRight, insetBottom, insetLeft, rx, ry} = this; return create("svg:rect") .call(applyIndirectStyles, this, scales, dimensions) .call(applyDirectStyles, this) @@ -35,7 +39,9 @@ export class Frame extends Mark { .attr("y", marginTop + insetTop) .attr("width", width - marginLeft - marginRight - insetLeft - insetRight) .attr("height", height - marginTop - marginBottom - insetTop - insetBottom) - .node(); + .attr("rx", rx) + .attr("ry", ry) + .node(); } } diff --git a/test/output/frameCorners.svg b/test/output/frameCorners.svg new file mode 100644 index 0000000000..6c4b4e19c7 --- /dev/null +++ b/test/output/frameCorners.svg @@ -0,0 +1,17 @@ + + + + \ No newline at end of file diff --git a/test/plots/frame-corners.js b/test/plots/frame-corners.js new file mode 100644 index 0000000000..5251a4a732 --- /dev/null +++ b/test/plots/frame-corners.js @@ -0,0 +1,5 @@ +import * as Plot from "@observablehq/plot"; + +export default async function() { + return Plot.frame({rx: 16, ry: 10}).plot(); +} diff --git a/test/plots/index.js b/test/plots/index.js index ed83a78b22..7b97e6992b 100644 --- a/test/plots/index.js +++ b/test/plots/index.js @@ -69,6 +69,7 @@ export {default as flareCluster} from "./flare-cluster.js"; export {default as flareIndent} from "./flare-indent.js"; export {default as flareTree} from "./flare-tree.js"; export {default as footballCoverage} from "./football-coverage.js"; +export {default as frameCorners} from "./frame-corners.js"; export {default as fruitSales} from "./fruit-sales.js"; export {default as fruitSalesDate} from "./fruit-sales-date.js"; export {default as gistempAnomaly} from "./gistemp-anomaly.js";