diff --git a/docs/features/plots.md b/docs/features/plots.md index 1770965608..5cf465401d 100644 --- a/docs/features/plots.md +++ b/docs/features/plots.md @@ -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 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} diff --git a/src/context.d.ts b/src/context.d.ts index 84b8d13646..ce2c3568d8 100644 --- a/src/context.d.ts +++ b/src/context.d.ts @@ -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 { @@ -16,4 +17,7 @@ export interface Context { /** The current projection, if any. */ projection?: GeoStreamWrapper; + + /** The default clip for all marks. */ + clip?: MarkOptions["clip"]; } diff --git a/src/context.js b/src/context.js index c1911f9118..32faa96f3a 100644 --- a/src/context.js +++ b/src/context.js @@ -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}) { diff --git a/src/mark.js b/src/mark.js index 831048fa4a..4cd3d854ab 100644 --- a/src/mark.js +++ b/src/mark.js @@ -22,7 +22,7 @@ export class Mark { marginRight = margin, marginBottom = margin, marginLeft = margin, - clip, + clip = defaults?.clip, channels: extraChannels, tip, render diff --git a/src/marks/axis.js b/src/marks/axis.js index 11984ca935..c7c7312d3f 100644 --- a/src/marks/axis.js +++ b/src/marks/axis.js @@ -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; } diff --git a/src/marks/frame.js b/src/marks/frame.js index 43abe05159..372678cf33 100644 --- a/src/marks/frame.js +++ b/src/marks/frame.js @@ -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 { diff --git a/src/plot.d.ts b/src/plot.d.ts index fbf407d806..e222369387 100644 --- a/src/plot.d.ts +++ b/src/plot.d.ts @@ -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"; @@ -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 /** diff --git a/src/style.js b/src/style.js index b3e046cc91..decebe5ed9 100644 --- a/src/style.js +++ b/src/style.js @@ -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 @@ -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(); diff --git a/test/plots/band-clip.ts b/test/plots/band-clip.ts index 34380664ce..ec83f38ab4 100644 --- a/test/plots/band-clip.ts +++ b/test/plots/band-clip.ts @@ -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})] }); }