Skip to content

Commit

Permalink
top-level clip option (observablehq#1792)
Browse files Browse the repository at this point in the history
  • Loading branch information
mbostock authored and chaichontat committed Jan 14, 2024
1 parent e3ae313 commit e763377
Show file tree
Hide file tree
Showing 9 changed files with 27 additions and 28 deletions.
2 changes: 2 additions & 0 deletions docs/features/plots.md
Original file line number Diff line number Diff line change
Expand Up @@ -270,6 +270,8 @@ Unitless numbers ([quirky lengths](https://www.w3.org/TR/css-values-4/#deprecate

The generated SVG element has a class name which applies a default stylesheet. Use the top-level **className** option to specify that class name.

The **clip** option <VersionBadge pr="1792" /> determines the default clipping behavior if the [mark **clip** option](./marks.md#mark-options) is not specified; set it to true to enable clipping. This option does not affect [axis](../marks/axis.md), [grid](../marks/grid.md), and [frame](../marks/frame.md) marks, whose **clip** option defaults to false.

The **document** option specifies the [document](https://developer.mozilla.org/en-US/docs/Web/API/Document) used to create plot elements. It defaults to window.document, but can be changed to another document, say when using a virtual DOM implementation for server-side rendering in Node.

## plot(*options*) {#plot}
Expand Down
4 changes: 4 additions & 0 deletions src/context.d.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import type {GeoStreamWrapper} from "d3";
import type {MarkOptions} from "./mark.js";

/** Additional rendering context provided to marks and initializers. */
export interface Context {
Expand All @@ -16,4 +17,7 @@ export interface Context {

/** The current projection, if any. */
projection?: GeoStreamWrapper;

/** The default clip for all marks. */
clip?: MarkOptions["clip"];
}
5 changes: 3 additions & 2 deletions src/context.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import {creator, select} from "d3";
import {maybeClip} from "./style.js";

export function createContext(options = {}) {
const {document = typeof window !== "undefined" ? window.document : undefined} = options;
return {document};
const {document = typeof window !== "undefined" ? window.document : undefined, clip} = options;
return {document, clip: maybeClip(clip)};
}

export function create(name, {document}) {
Expand Down
2 changes: 1 addition & 1 deletion src/mark.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ export class Mark {
marginRight = margin,
marginBottom = margin,
marginLeft = margin,
clip,
clip = defaults?.clip,
channels: extraChannels,
tip,
render
Expand Down
1 change: 1 addition & 0 deletions src/marks/axis.js
Original file line number Diff line number Diff line change
Expand Up @@ -564,6 +564,7 @@ function axisMark(mark, k, ariaLabel, data, options, initialize) {
channels = {};
}
m.ariaLabel = ariaLabel;
if (m.clip === undefined) m.clip = false; // don’t clip axes by default
return m;
}

Expand Down
6 changes: 4 additions & 2 deletions src/marks/frame.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,16 @@ import {applyChannelStyles, applyDirectStyles, applyIndirectStyles, applyTransfo
const defaults = {
ariaLabel: "frame",
fill: "none",
stroke: "currentColor"
stroke: "currentColor",
clip: false
};

const lineDefaults = {
ariaLabel: "frame",
fill: null,
stroke: "currentColor",
strokeLinecap: "square"
strokeLinecap: "square",
clip: false
};

export class Frame extends Mark {
Expand Down
5 changes: 4 additions & 1 deletion src/plot.d.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import type {ChannelValue} from "./channel.js";
import type {LegendOptions} from "./legends.js";
import type {Data, Markish} from "./mark.js";
import type {Data, MarkOptions, Markish} from "./mark.js";
import type {ProjectionFactory, ProjectionImplementation, ProjectionName, ProjectionOptions} from "./projection.js";
import type {Scale, ScaleDefaults, ScaleName, ScaleOptions} from "./scales.js";

Expand Down Expand Up @@ -146,6 +146,9 @@ export interface PlotOptions extends ScaleDefaults {
*/
document?: Document;

/** The default clip for all marks. */
clip?: MarkOptions["clip"];

// scale, axis, and legend definitions

/**
Expand Down
19 changes: 6 additions & 13 deletions src/style.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,8 @@ import {geoPath, group, namespaces} from "d3";
import {create} from "./context.js";
import {defined, nonempty} from "./defined.js";
import {formatDefault} from "./format.js";
import {
string,
number,
maybeColorChannel,
maybeNumberChannel,
maybeKeyword,
isNoneish,
isNone,
isRound,
keyof
} from "./options.js";
import {isNone, isNoneish, isRound, maybeColorChannel, maybeNumberChannel} from "./options.js";
import {keyof, keyword, number, string} from "./options.js";
import {warn} from "./warnings.js";

export const offset = (typeof window !== "undefined" ? window.devicePixelRatio > 1 : typeof it === "undefined") ? 0 : 0.5; // prettier-ignore
Expand Down Expand Up @@ -311,13 +302,15 @@ export function* groupIndex(I, position, mark, channels) {
export function maybeClip(clip) {
if (clip === true) clip = "frame";
else if (clip === false) clip = null;
return maybeKeyword(clip, "clip", ["frame", "sphere"]);
else if (clip != null) clip = keyword(clip, "clip", ["frame", "sphere"]);
return clip;
}

// Note: may mutate selection.node!
function applyClip(selection, mark, dimensions, context) {
let clipUrl;
switch (mark.clip) {
const {clip = context.clip} = mark;
switch (clip) {
case "frame": {
const {width, height, marginLeft, marginRight, marginTop, marginBottom} = dimensions;
const id = getClipId();
Expand Down
11 changes: 2 additions & 9 deletions test/plots/band-clip.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,8 @@ import * as d3 from "d3";
export async function bandClip() {
return Plot.plot({
y: {type: "band"},
marks: [
Plot.frame(),
Plot.text(["A", "B", "C"], {
x: (d) => d,
y: (d) => d,
clip: true,
fontSize: 50
})
]
clip: true,
marks: [Plot.frame(), Plot.text(["A", "B", "C"], {x: (d) => d, y: (d) => d, fontSize: 50})]
});
}

Expand Down

0 comments on commit e763377

Please sign in to comment.