Skip to content

Commit

Permalink
api index (#1765)
Browse files Browse the repository at this point in the history
* api index

* fix crosshair link

* externalize list style
  • Loading branch information
mbostock authored Jul 29, 2023
1 parent e14b05c commit a81b65e
Show file tree
Hide file tree
Showing 8 changed files with 401 additions and 183 deletions.
3 changes: 2 additions & 1 deletion docs/.vitepress/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,8 @@ export default defineConfig({
{text: "Crosshair", link: "/interactions/crosshair"},
{text: "Pointer", link: "/interactions/pointer"}
]
}
},
{text: "API index", link: "/api"}
],
search: {
provider: "local"
Expand Down
34 changes: 34 additions & 0 deletions docs/api.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
<script setup>

import {data} from "./data/api.data";

</script>

# API index

## Methods

<ul :class="$style.oneline">
<li v-for="({name, href, comment}) in data.methods">
<span><a :href="href">{{ name }}</a> - {{ comment }}</span>
</li>
</ul>

## Options

<ul>
<li v-for="[name, contexts] in data.options">
<b>{{ name }}</b> - <span v-for="({name, href}, index) in contexts"><a :href="href">{{ name }}</a><span v-if="index < contexts.length - 1">, </span></span>
</li>
</ul>

<style module>

ul.oneline span {
display: block;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}

</style>
140 changes: 140 additions & 0 deletions docs/data/api.data.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
import {rollup, sort} from "d3";
import {Node, FunctionDeclaration, InterfaceDeclaration, Project, VariableDeclaration, VariableStatement} from "ts-morph";

// These interfaces tend to represent things that Plot constructs internally,
// rather than objects that the user is expected to provide.
function isInternalInterface(name) {
return (
name === "AutoSpec" ||
name === "Channel" ||
name === "ChannelDomainOptions" || // TODO
name === "ChannelTransform" ||
name === "Context" ||
name === "Dimensions" ||
name === "Plot" ||
name === "Scale"
);
}

// This tries to get a brief human-readable, one-sentence summary description of
// the exported symbol. We might want to formalize this so that we can be more
// intentional when authoring documentation.
function getDescription(node: FunctionDeclaration | VariableStatement): string {
return node
.getJsDocs()[0]
?.getDescription()
.replace(/\n/g, " ") // replace newlines with spaces
.replace(/[*_]/g, "") // remove bold and italics formatting
.replace(/[.:]($|\s+).*/g, "") // truncate at the first period or colon
.replace(/\[([^[]+)\]\[\d+\]/g, "$1") // strip links (assuming [1] syntax)
.trim();
}

// While we try to keep the source code file structure as close as possible to
// the documentation URL structure, there are some inevitable deviations that
// are codified by this function. When new files are added, please try to keep
// this function up-to-date, and try to generalize patterns so that we
// automatically generate correct links. (TODO Verify that the links are valid.)
function getHref(name: string, path: string): string {
path = path.replace(/\.d\.ts$/, ""); // drop trailing .d.ts
path = path.replace(/([a-z0-9])([A-Z])/, (_, a, b) => `${a}-${b.toLowerCase()}`); // camel case conversion
if (path.split("/").length === 1) path = `features/${path}`; // top-level declarations are features
switch (path) {
case "features/curve":
case "features/format":
case "features/mark":
case "features/plot":
return `${path}s`;
case "features/options":
return "features/transforms";
case "marks/axis": {
switch (name) {
case "gridX":
case "gridY":
case "gridFx":
case "gridFy":
return "marks/grid";
}
break;
}
case "marks/crosshair":
return "interactions/crosshair";
case "transforms/basic": {
switch (name) {
case "filter":
return "transforms/filter";
case "reverse":
case "shuffle":
case "sort":
return "transforms/sort";
}
return "features/transforms";
}
}
return path;
}

function getInterfaceName(name: string, path: string): string {
name = name.replace(/(Transform|Corner|X|Y|Output)?(Defaults|Options|Styles)$/, "");
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 (path.startsWith("marks/")) name += " mark";
else if (path.startsWith("transforms/")) name += " transform";
return name;
}

export default {
watch: [],
async load() {
const project = new Project({tsConfigFilePath: "tsconfig.json"});
const allMethods: {name: string; comment: string; href: string}[] = [];
const allOptions: {name: string; context: {name: string; href: string}}[] = [];
const index = project.getSourceFile("src/index.d.ts")!;
for (const [name, declarations] of index.getExportedDeclarations()) {
for (const declaration of declarations) {
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}});
}
} else if (Node.isFunctionDeclaration(declaration)) {
const comment = getDescription(declaration);
if (comment) {
const href = getHref(name, index.getRelativePathTo(declaration.getSourceFile()));
allMethods.push({name, comment, href});
}
} else if (Node.isVariableDeclaration(declaration)) {
const comment = getDescription(declaration.getVariableStatement()!);
if (comment) {
const href = getHref(name, index.getRelativePathTo(declaration.getSourceFile()));
allMethods.push({name, comment, href});
}
}
}
}
return {
methods: sort(allMethods, ({name}) => name),
options: sort(
rollup(
allOptions,
(D) =>
sort(
rollup(
D.map((d) => d.context),
([d]) => d,
(d) => d.name
).values(),
(d) => d.name
),
(d) => d.name
),
([name]) => name
)
};
}
};
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@
"prettier": "^3.0.0",
"rollup": "^3.7.0",
"topojson-client": "^3.1.0",
"ts-morph": "^19.0.0",
"typescript": "^5.0.2",
"vite": "^4.0.0",
"vitepress": "^1.0.0-beta.2"
Expand Down
2 changes: 1 addition & 1 deletion src/mark.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -477,5 +477,5 @@ export class RenderableMark extends Mark {
/** A compound Mark, comprising other marks. */
export type CompoundMark = Markish[] & Pick<Mark, "plot">;

/** Given an array of marks, returns a compound mark; supports *mark.plot shorthand. */
/** Given an array of marks, returns a compound mark; supports *mark*.plot shorthand. */
export function marks(...marks: Markish[]): CompoundMark;
20 changes: 11 additions & 9 deletions src/marks/area.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ export interface AreaYOptions extends Omit<AreaOptions, "x1" | "x2">, BinOptions
}

/**
* Returns a new area with the given *data* and *options*. The area mark is
* Returns a new area mark with the given *data* and *options*. The area mark is
* rarely used directly; it is only needed when the baseline and topline have
* neither *x* nor *y* values in common. Use areaY for a horizontal orientation
* where the baseline and topline share *x* values, or areaX for a vertical
Expand All @@ -134,9 +134,10 @@ export interface AreaYOptions extends Omit<AreaOptions, "x1" | "x2">, BinOptions
export function area(data?: Data, options?: AreaOptions): Area;

/**
* Returns a new vertically-oriented area for the given *data* and *options*,
* where the baseline and topline share **y** values, as in a time-series area
* chart where time goes up↑. For example, to plot Apple’s daily stock price:
* Returns a new vertically-oriented area mark for the given *data* and
* *options*, where the baseline and topline share **y** values, as in a
* time-series area chart where time goes up↑. For example, to plot Apple’s
* daily stock price:
*
* ```js
* Plot.areaX(aapl, {y: "Date", x: "Close"})
Expand Down Expand Up @@ -165,9 +166,10 @@ export function area(data?: Data, options?: AreaOptions): Area;
export function areaX(data?: Data, options?: AreaXOptions): Area;

/**
* Returns a new horizontally-oriented area for the given *data* and *options*,
* where the baseline and topline share **x** values, as in a time-series area
* chart where time goes right→. For example, to plot Apple’s daily stock price:
* Returns a new horizontally-oriented area mark for the given *data* and
* *options*, where the baseline and topline share **x** values, as in a
* time-series area chart where time goes right→. For example, to plot Apple’s
* daily stock price:
*
* ```js
* Plot.areaY(aapl, {x: "Date", y: "Close"})
Expand All @@ -179,8 +181,8 @@ export function areaX(data?: Data, options?: AreaXOptions): Area;
* specified, the other defaults to **y**, which defaults to zero.
*
* If an **interval** is specified, **x** values are binned accordingly,
* allowing zeroes for empty bins instead of interpolating across gaps. This
* is recommended to “regularize” sampled data; for example, if your data
* allowing zeroes for empty bins instead of interpolating across gaps. This is
* recommended to “regularize” sampled data; for example, if your data
* represents timestamped observations and you expect one observation per day,
* use *day* as the **interval**.
*
Expand Down
8 changes: 4 additions & 4 deletions src/marks/line.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,10 +83,10 @@ export interface LineYOptions extends LineOptions, BinOptions {
}

/**
* Returns a new line for the given *data* and *options* by connecting control
* points. If neither the **x** nor **y** options are specified, *data* is
* assumed to be an array of pairs [[*x₀*, *y₀*], [*x₁*, *y₁*], [*x₂*, *y₂*], …]
* such that **x** = [*x₀*, *x₁*, *x₂*, …] and **y** = [*y₀*, *y₁*, *y₂*, …].
* Returns a new line mark for the given *data* and *options* by connecting
* control points. If neither the **x** nor **y** options are specified, *data*
* is assumed to be an array of pairs [[*x₀*, *y₀*], [*x₁*, *y₁*], [*x₂*, *y₂*],
* …] such that **x** = [*x₀*, *x₁*, *x₂*, …] and **y** = [*y₀*, *y₁*, *y₂*, …].
*
* Points along the line are connected in input order. If there are multiple
* series via the **z**, **fill**, or **stroke** channel, series are drawn in
Expand Down
Loading

0 comments on commit a81b65e

Please sign in to comment.