Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

api index #1765

Merged
merged 3 commits into from
Jul 29, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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