mayacharts 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +619 -0
- package/dist/core/a11y.d.ts +10 -0
- package/dist/core/format.d.ts +2 -0
- package/dist/core/layout.d.ts +22 -0
- package/dist/core/marks/bar.d.ts +2 -0
- package/dist/core/marks/dumbbell.d.ts +3 -0
- package/dist/core/marks/heatmap.d.ts +3 -0
- package/dist/core/marks/kpi.d.ts +2 -0
- package/dist/core/marks/line.d.ts +4 -0
- package/dist/core/marks/scatter.d.ts +2 -0
- package/dist/core/registry.d.ts +13 -0
- package/dist/core/render.d.ts +8 -0
- package/dist/core/scale.d.ts +3 -0
- package/dist/core/shape.d.ts +14 -0
- package/dist/core/strings.d.ts +37 -0
- package/dist/core/svg.d.ts +13 -0
- package/dist/core/ticks.d.ts +2 -0
- package/dist/core/types.d.ts +424 -0
- package/dist/core/validate.d.ts +24 -0
- package/dist/element/animate.d.ts +13 -0
- package/dist/element/drill.d.ts +12 -0
- package/dist/element/html.d.ts +1 -0
- package/dist/element/maya-chart.d.ts +21 -0
- package/dist/element/measure.d.ts +9 -0
- package/dist/element/select.d.ts +14 -0
- package/dist/element/tooltip.d.ts +10 -0
- package/dist/element/zoom.d.ts +11 -0
- package/dist/element.d.ts +15 -0
- package/dist/element.js +4 -0
- package/dist/element.js.map +1 -0
- package/dist/flow.d.ts +3 -0
- package/dist/flow.js +3 -0
- package/dist/flow.js.map +1 -0
- package/dist/geo.d.ts +3 -0
- package/dist/geo.js +3 -0
- package/dist/geo.js.map +1 -0
- package/dist/global.d.ts +4 -0
- package/dist/hierarchy.d.ts +15 -0
- package/dist/hierarchy.js +3 -0
- package/dist/hierarchy.js.map +1 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -0
- package/dist/maya.global.js +4 -0
- package/dist/maya.global.js.map +1 -0
- package/dist/styles/theme.d.ts +1 -0
- package/dist/theme.css +1 -0
- package/llms.txt +102 -0
- package/package.json +105 -0
- package/schema.json +381 -0
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { Mark } from "./types.ts";
|
|
2
|
+
export declare const VERSION = "0.1.0";
|
|
3
|
+
export declare const MAJOR = "0";
|
|
4
|
+
/** type -> Mark for every registered module type (shared across bundles). */
|
|
5
|
+
export declare const MODULES: Map<string, Mark>;
|
|
6
|
+
/** Types that ship in core (static CORE map in render.ts). */
|
|
7
|
+
export declare const CORE_TYPES: readonly ["bar", "line", "area", "scatter", "heatmap", "waterfall", "kpi", "dumbbell"];
|
|
8
|
+
/** Module types and the import that provides them (for the unknown-type hint). */
|
|
9
|
+
export declare const MODULE_OF: Readonly<Record<string, string>>;
|
|
10
|
+
/** Not exported from the public entry: modules call it on import. First wins. */
|
|
11
|
+
export declare function register(type: string, mark: Mark): void;
|
|
12
|
+
/** Registered module types. */
|
|
13
|
+
export declare const types: () => string[];
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { ChartSpec, Parts, RenderOptions, Row } from "./types.ts";
|
|
2
|
+
export declare function renderParts<R extends object = Row>(spec: ChartSpec<R> | ChartSpec, opts?: RenderOptions): Parts;
|
|
3
|
+
/** Standalone SVG string with an embedded stylesheet. */
|
|
4
|
+
export declare function render<R extends object = Row>(spec: ChartSpec<R> | ChartSpec, opts?: RenderOptions): string;
|
|
5
|
+
/** Inner shadow-root markup, shared by renderShell and the element. Fixed slot order. */
|
|
6
|
+
export declare function shellInner(parts: Parts, css: string, nonce?: string): string;
|
|
7
|
+
/** `<maya-chart>` with Declarative Shadow DOM. JSON child escapes `<` as <. */
|
|
8
|
+
export declare function renderShell<R extends object = Row>(spec: ChartSpec<R> | ChartSpec, opts?: RenderOptions): string;
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import type { BandScale, LinearScale } from "./types.ts";
|
|
2
|
+
export declare function bandScale(domain: readonly string[], range: readonly [number, number], padInner?: number, padOuter?: number): BandScale;
|
|
3
|
+
export declare function linearScale(domain: readonly [number, number], range: readonly [number, number]): LinearScale;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { Aggregate, ResolvedSpec, Shaped, View } from "./types.ts";
|
|
2
|
+
/** Reducer for one aggregate; shared with hierarchy.ts. Empty: null (count: 0). */
|
|
3
|
+
export declare function agg(kind: Aggregate): {
|
|
4
|
+
add(v: number): void;
|
|
5
|
+
value(): number | null;
|
|
6
|
+
};
|
|
7
|
+
interface Opts {
|
|
8
|
+
hidden?: readonly string[];
|
|
9
|
+
window?: View["window"];
|
|
10
|
+
}
|
|
11
|
+
export declare function shape(s: ResolvedSpec, o?: Opts | readonly string[]): Shaped;
|
|
12
|
+
/** colorBy field aggregated per (category, series) like the marks; null without a field colorBy. */
|
|
13
|
+
export declare function colorVals(s: ResolvedSpec): (c: string, ser: string) => number | null;
|
|
14
|
+
export {};
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
export declare const TEXT: {
|
|
2
|
+
readonly noData: "No data";
|
|
3
|
+
readonly other: "Other";
|
|
4
|
+
readonly back: "Back";
|
|
5
|
+
readonly reset: "Reset zoom";
|
|
6
|
+
/** Live region after a measure toggle: {0} = measure title. */
|
|
7
|
+
readonly showing: "Showing {0}";
|
|
8
|
+
/** Live region after drill: {0} = branch path joined with " / ". */
|
|
9
|
+
readonly drilledInto: "Drilled into {0}";
|
|
10
|
+
/** Live region after zoom: {0}, {1} = first and last category (or range ends). */
|
|
11
|
+
readonly zoomedTo: "Zoomed to {0} – {1}";
|
|
12
|
+
/** Live region after selection: {0} = count. */
|
|
13
|
+
readonly selected: "{0} selected";
|
|
14
|
+
readonly selectionCleared: "Selection cleared";
|
|
15
|
+
/** Auto description: {0} = noun, {1} = y title, {2} = x title. */
|
|
16
|
+
readonly chartOf: "{0} chart of {1} by {2}";
|
|
17
|
+
/** Table caption when rows are capped: {0} = shown, {1} = total. */
|
|
18
|
+
readonly firstOf: "First {0} of {1} rows";
|
|
19
|
+
/** aria-label of the measure radiogroup. */
|
|
20
|
+
readonly measures: "Measure";
|
|
21
|
+
/** aria-label of the breadcrumb nav. */
|
|
22
|
+
readonly crumbs: "Drill path";
|
|
23
|
+
/** Tone text (never colour alone): colorBy "sign" and { target }. */
|
|
24
|
+
readonly positive: "positive";
|
|
25
|
+
readonly negative: "negative";
|
|
26
|
+
readonly above: "above target";
|
|
27
|
+
readonly below: "below target";
|
|
28
|
+
/** kpi delta: {0} = signed change, {1} = previous period. */
|
|
29
|
+
readonly vs: "{0} vs {1}";
|
|
30
|
+
/** kpi bullet: {0} = share of target, {1} = target. */
|
|
31
|
+
readonly ofTarget: "{0} of target {1}";
|
|
32
|
+
};
|
|
33
|
+
export type TextKey = keyof typeof TEXT;
|
|
34
|
+
/** Localised string for `key` with `{n}` placeholders filled from `args`. */
|
|
35
|
+
export declare function t(spec: {
|
|
36
|
+
text?: Partial<Record<TextKey, string>> | undefined;
|
|
37
|
+
}, key: TextKey, ...args: (string | number)[]): string;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export declare function esc(s: string | number): string;
|
|
2
|
+
export declare function r(n: number): number;
|
|
3
|
+
export declare function el(tag: string, attrs: Record<string, string | number | boolean | null | undefined>, children?: string): string;
|
|
4
|
+
/** Sentinel category for `limit`'s rolled-up rest. Cannot collide with real data text. */
|
|
5
|
+
export declare const OTHER = "\0other";
|
|
6
|
+
/**
|
|
7
|
+
* Mark identity: each part `encodeURIComponent`ed (lone surrogates replaced, `~` -> %7E),
|
|
8
|
+
* joined by `~`. Grammar: band `S~C`; line/area `l~S`/`a~S`; scatter `S~name` (or index);
|
|
9
|
+
* hierarchy `h~p0~p1…`; sankey node `n~depth~name`, link `k~depth~src~dst`; hexmap `g~CODE`.
|
|
10
|
+
*/
|
|
11
|
+
export declare const key: (...parts: unknown[]) => string;
|
|
12
|
+
/** Invisible target grown to >= 24 px around a small rect mark; "" when it is big enough. */
|
|
13
|
+
export declare function hit(d: Parameters<typeof el>[1], x: number, y: number, w: number, h: number): string;
|
|
@@ -0,0 +1,424 @@
|
|
|
1
|
+
import type { TextKey } from "./strings.ts";
|
|
2
|
+
export type { TextKey };
|
|
3
|
+
/** A data row. Field values are read by name via `spec.x`, `spec.y`, `spec.series`… */
|
|
4
|
+
export type Row = Readonly<Record<string, string | number | boolean | null | undefined>>;
|
|
5
|
+
export type ChartType = "bar" | "line" | "area" | "scatter" | "heatmap" | "waterfall" | "kpi" | "dumbbell" | "treemap" | "sunburst" | "sankey" | "hexmap";
|
|
6
|
+
export type Aggregate = "sum" | "mean" | "count" | "min" | "max";
|
|
7
|
+
export type NumberPreset = "auto" | "integer" | "decimal" | "compact" | "percent" | "currency";
|
|
8
|
+
export type DatePreset = "date" | "month" | "year" | "time" | "datetime";
|
|
9
|
+
export type FormatPreset = NumberPreset | DatePreset;
|
|
10
|
+
/** Text added around a formatted value. */
|
|
11
|
+
export interface Affix {
|
|
12
|
+
prefix?: string;
|
|
13
|
+
suffix?: string;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* One field's format: a preset, or Intl options plus `prefix`/`suffix`. Options holding
|
|
17
|
+
* any date key (`year`, `month`, `dateStyle`, `timeZone`…) are date options; else number.
|
|
18
|
+
*/
|
|
19
|
+
export type FieldFormat = FormatPreset | (Intl.NumberFormatOptions & Affix) | (Intl.DateTimeFormatOptions & Affix);
|
|
20
|
+
export type ThemeToken = "font" | "fontSize" | "fg" | "fgMuted" | "grid" | "bg" | "accent" | "radius" | "tooltipBg" | "tooltipFg" | "focus" | "good" | "bad" | "series1" | "series2" | "series3" | "series4" | "series5" | "series6" | "series7" | "series8";
|
|
21
|
+
/** Field names of row type `R` (autocompletes for typed rows; `string` for `Row`). */
|
|
22
|
+
export type Field<R> = Extract<keyof R, string>;
|
|
23
|
+
/**
|
|
24
|
+
* The whole public API surface. Plain JSON on purpose: it must stay serializable,
|
|
25
|
+
* LLM-generatable, diffable and hashable. Never add function-valued options.
|
|
26
|
+
* Rule: `x` is always the category and `y` always the value, whatever the orientation.
|
|
27
|
+
*/
|
|
28
|
+
export interface ChartSpec<R extends object = Row> {
|
|
29
|
+
/** Ignored; lets editors and LLMs find the JSON schema.
|
|
30
|
+
* @example "$schema": "https://unpkg.com/mayacharts/schema.json" */
|
|
31
|
+
$schema?: string;
|
|
32
|
+
/** Chart type. treemap/sunburst need `mayacharts/hierarchy`, sankey `flow`, hexmap `geo`.
|
|
33
|
+
* @example type: "bar" */
|
|
34
|
+
type: ChartType;
|
|
35
|
+
/** Row objects.
|
|
36
|
+
* @example data: [{ month: "Jan", revenue: 10 }] */
|
|
37
|
+
data: readonly R[];
|
|
38
|
+
/** How rows sharing a (category, series) combine. `count` counts non-null y. Default "sum".
|
|
39
|
+
* @example aggregate: "mean" */
|
|
40
|
+
aggregate?: Aggregate;
|
|
41
|
+
/** Order categories by total across all series (hidden ones too). Default: data order.
|
|
42
|
+
* @example sort: "desc" */
|
|
43
|
+
sort?: "asc" | "desc";
|
|
44
|
+
/** Keep the top N categories by total; the rest roll up into a final "Other".
|
|
45
|
+
* @example limit: 10 */
|
|
46
|
+
limit?: number;
|
|
47
|
+
/** Category field (scatter: numeric x; hexmap: US state; kpi: optional period, last one is the headline). Not used by path types.
|
|
48
|
+
* @example x: "month" */
|
|
49
|
+
x?: Field<R>;
|
|
50
|
+
/** Value field; an array adds a measure toggle, first one active.
|
|
51
|
+
* @example y: ["revenue", "units"] */
|
|
52
|
+
y: Field<R> | readonly Field<R>[];
|
|
53
|
+
/** Splits rows into series (heatmap: the row category; dumbbell: exactly two, from and to). bar line area scatter heatmap dumbbell.
|
|
54
|
+
* @example series: "region" */
|
|
55
|
+
series?: Field<R>;
|
|
56
|
+
/** Hierarchy fields, outer to inner. treemap sunburst sankey; bar/line/area with `drill` (replaces `x`).
|
|
57
|
+
* @example path: ["region", "state"] */
|
|
58
|
+
path?: readonly Field<R>[];
|
|
59
|
+
/** Bubble area field (sqrt scale). scatter only.
|
|
60
|
+
* @example size: "population" */
|
|
61
|
+
size?: Field<R>;
|
|
62
|
+
/** Point identity and tooltip title. scatter only.
|
|
63
|
+
* @example name: "country" */
|
|
64
|
+
name?: Field<R>;
|
|
65
|
+
/** x values drawn as running-total bars. waterfall only.
|
|
66
|
+
* @example totals: ["Q1", "FY"] */
|
|
67
|
+
totals?: readonly string[];
|
|
68
|
+
/** Stack series instead of grouping them. bar and area only.
|
|
69
|
+
* @example stack: true */
|
|
70
|
+
stack?: boolean;
|
|
71
|
+
/** Categories on the left axis. bar and dumbbell.
|
|
72
|
+
* @example horizontal: true */
|
|
73
|
+
horizontal?: boolean;
|
|
74
|
+
/** Second value field, drawn as a line on a right axis over the bars. Vertical bar only.
|
|
75
|
+
* @example y2: "units" */
|
|
76
|
+
y2?: Field<R>;
|
|
77
|
+
/** A preset for every `y`, or a preset / Intl options per field. Display only.
|
|
78
|
+
* @example format: { revenue: "currency", month: "month", margin: { style: "percent", suffix: " gm" } } */
|
|
79
|
+
format?: FormatPreset | Readonly<Partial<Record<Field<R>, FieldFormat>>>;
|
|
80
|
+
/** Display names by field: axis titles (shown only when set), tooltip, legend, table.
|
|
81
|
+
* @example titles: { revenue: "Revenue ($)" } */
|
|
82
|
+
titles?: Readonly<Partial<Record<Field<R>, string>>>;
|
|
83
|
+
/** Formatted values on marks. Default false (heatmap: true when cells are ≥ 24 px).
|
|
84
|
+
* @example labels: true */
|
|
85
|
+
labels?: boolean;
|
|
86
|
+
/** Localised UI strings with `{0}` placeholders (see strings.ts for keys).
|
|
87
|
+
* @example text: { noData: "Keine Daten", back: "Zurück" } */
|
|
88
|
+
text?: Readonly<Partial<Record<TextKey, string>>>;
|
|
89
|
+
/** BCP 47 locale for formatting. Default "en-US" (deterministic SSR).
|
|
90
|
+
* @example locale: "de-DE" */
|
|
91
|
+
locale?: string;
|
|
92
|
+
/** ISO 4217 code for the "currency" preset. Default "USD".
|
|
93
|
+
* @example currency: "EUR" */
|
|
94
|
+
currency?: string;
|
|
95
|
+
/** Visible heading, also the accessible name.
|
|
96
|
+
* @example title: "Revenue by month" */
|
|
97
|
+
title?: string;
|
|
98
|
+
/** Accessible description; auto-generated when omitted.
|
|
99
|
+
* @example description: "Revenue doubled from January to December." */
|
|
100
|
+
description?: string;
|
|
101
|
+
/** Fixed value-axis domain. Not allowed with a `y` array.
|
|
102
|
+
* @example yDomain: [0, 100] */
|
|
103
|
+
yDomain?: readonly [number, number];
|
|
104
|
+
/** Fixed x domain. scatter only.
|
|
105
|
+
* @example xDomain: [0, 1] */
|
|
106
|
+
xDomain?: readonly [number, number];
|
|
107
|
+
/** Hover/keyboard tooltip. Default true.
|
|
108
|
+
* @example tooltip: false */
|
|
109
|
+
tooltip?: boolean;
|
|
110
|
+
/** Legend; clicking toggles series. Default: true when `series` is set.
|
|
111
|
+
* @example legend: false */
|
|
112
|
+
legend?: boolean;
|
|
113
|
+
/** Click/Enter zooms into a branch of `path`; breadcrumb, Back and Escape pop.
|
|
114
|
+
* @example drill: true */
|
|
115
|
+
drill?: boolean;
|
|
116
|
+
/** Click/Enter/legend selects marks, others dim; Escape clears. Not with `drill`.
|
|
117
|
+
* @example select: "multi" */
|
|
118
|
+
select?: true | "multi";
|
|
119
|
+
/** Drag to zoom. line, area, scatter. Reset chip, double-click and Escape restore.
|
|
120
|
+
* @example zoom: true */
|
|
121
|
+
zoom?: boolean;
|
|
122
|
+
/** Animate updates (element only). Default true.
|
|
123
|
+
* @example animate: false */
|
|
124
|
+
animate?: boolean;
|
|
125
|
+
/** Palette in series order (max 8), or colours by series value.
|
|
126
|
+
* @example colors: { North: "#0b6", South: "oklch(.6 .17 30)" } */
|
|
127
|
+
colors?: readonly string[] | Readonly<Record<string, string>>;
|
|
128
|
+
/** Tone by sign of y, by a target, or a ramp by a numeric field. Not with `series` (dumbbell: "sign" of to minus from; kpi: target only, drawn as a bullet bar).
|
|
129
|
+
* @example colorBy: { target: 100 } */
|
|
130
|
+
colorBy?: "sign" | {
|
|
131
|
+
readonly target: number;
|
|
132
|
+
} | Field<R>;
|
|
133
|
+
/** Theme token overrides (CSS values, allowlisted).
|
|
134
|
+
* @example theme: { accent: "#0b6", font: "'Inter', sans-serif" } */
|
|
135
|
+
theme?: Readonly<Partial<Record<ThemeToken, string>>>;
|
|
136
|
+
/** Grid lines perpendicular to the value axis. Default true.
|
|
137
|
+
* @example grid: false */
|
|
138
|
+
grid?: boolean;
|
|
139
|
+
/** Bottom axis. Default true.
|
|
140
|
+
* @example xAxis: false */
|
|
141
|
+
xAxis?: boolean;
|
|
142
|
+
/** Left axis. Default true.
|
|
143
|
+
* @example yAxis: false */
|
|
144
|
+
yAxis?: boolean;
|
|
145
|
+
/** Visually hidden data table for screen readers. Default true.
|
|
146
|
+
* @example table: false */
|
|
147
|
+
table?: boolean;
|
|
148
|
+
}
|
|
149
|
+
/** A selected mark, by raw values (never formatted text). */
|
|
150
|
+
export interface Sel {
|
|
151
|
+
x?: unknown;
|
|
152
|
+
series?: unknown;
|
|
153
|
+
name?: unknown;
|
|
154
|
+
}
|
|
155
|
+
/** Interaction state outside the spec. SSR can render any of it. */
|
|
156
|
+
export interface View {
|
|
157
|
+
/** Active index into a `y` array. */
|
|
158
|
+
measure?: number;
|
|
159
|
+
/** Drilled branch: one raw value per `path` level, outer first. */
|
|
160
|
+
drill?: readonly string[];
|
|
161
|
+
/** Zoom: category index slice [i0, i1] (inclusive), or scatter box [x0, x1, y0, y1]. */
|
|
162
|
+
window?: readonly [number, number] | readonly [number, number, number, number];
|
|
163
|
+
/** Series keys hidden by the legend. */
|
|
164
|
+
hidden?: readonly string[];
|
|
165
|
+
}
|
|
166
|
+
export interface RenderOptions {
|
|
167
|
+
/** Pixel size of the plot box. Default 640 x 320. */
|
|
168
|
+
width?: number;
|
|
169
|
+
height?: number;
|
|
170
|
+
view?: View;
|
|
171
|
+
selected?: readonly Sel[];
|
|
172
|
+
/** CSP nonce for the shell's `<style>` (renderShell). */
|
|
173
|
+
nonce?: string;
|
|
174
|
+
}
|
|
175
|
+
export interface MayaSelectDetail {
|
|
176
|
+
selected: Sel[];
|
|
177
|
+
target: (Sel & {
|
|
178
|
+
value: unknown;
|
|
179
|
+
}) | null;
|
|
180
|
+
}
|
|
181
|
+
export type MayaViewDetail = View;
|
|
182
|
+
export interface MayaErrorDetail {
|
|
183
|
+
code: ErrorCode;
|
|
184
|
+
path: string;
|
|
185
|
+
message: string;
|
|
186
|
+
}
|
|
187
|
+
export type ErrorCode = "spec-not-object" | "missing-field" | "unknown-type" | "data-not-array" | "row-not-object" | "unknown-field" | "non-numeric-y" | "non-numeric-field" | "non-positive-value" | "unknown-option" | "invalid-option" | "option-unsupported" | "stack-unsupported" | "invalid-domain" | "invalid-format" | "invalid-theme" | "unsafe-css-value" | "invalid-size" | "unknown-state" | "too-many-marks";
|
|
188
|
+
/** Spec with every default and the view's measure/drill applied. From `resolve()`. */
|
|
189
|
+
export interface ResolvedSpec {
|
|
190
|
+
type: ChartType;
|
|
191
|
+
/** Rows after the drill filter. */
|
|
192
|
+
data: readonly Row[];
|
|
193
|
+
/** Category field ("" for path types). With `path` + drill: the current level. */
|
|
194
|
+
x: string;
|
|
195
|
+
/** Active measure. */
|
|
196
|
+
y: string;
|
|
197
|
+
/** Every measure (`[y]` when `spec.y` is a string). */
|
|
198
|
+
measures: string[];
|
|
199
|
+
/** Index of `y` in `measures`. */
|
|
200
|
+
measure: number;
|
|
201
|
+
series: string | null;
|
|
202
|
+
/** Right-axis line measure (bar only); null when unset. */
|
|
203
|
+
y2: string | null;
|
|
204
|
+
/** Remaining path levels below the drilled branch ([] when no path). */
|
|
205
|
+
path: string[];
|
|
206
|
+
/** Applied drill values, outer first ([] at the root). */
|
|
207
|
+
drilled: string[];
|
|
208
|
+
/** Scatter zoom box [x0, x1, y0, y1] from view.window; null otherwise. */
|
|
209
|
+
window: readonly [number, number, number, number] | null;
|
|
210
|
+
size: string | null;
|
|
211
|
+
name: string | null;
|
|
212
|
+
totals: string[];
|
|
213
|
+
stack: boolean;
|
|
214
|
+
horizontal: boolean;
|
|
215
|
+
aggregate: Aggregate;
|
|
216
|
+
sort: "asc" | "desc" | null;
|
|
217
|
+
limit: number | null;
|
|
218
|
+
/** Per-field format; a bare-string `spec.format` is expanded to every measure. */
|
|
219
|
+
format: ReadonlyMap<string, FieldFormat>;
|
|
220
|
+
titles: ReadonlyMap<string, string>;
|
|
221
|
+
/** null = the type's default. */
|
|
222
|
+
labels: boolean | null;
|
|
223
|
+
text: Partial<Record<TextKey, string>>;
|
|
224
|
+
title: string | null;
|
|
225
|
+
description: string | null;
|
|
226
|
+
legend: boolean;
|
|
227
|
+
tooltip: boolean;
|
|
228
|
+
drill: boolean;
|
|
229
|
+
select: false | true | "multi";
|
|
230
|
+
zoom: boolean;
|
|
231
|
+
grid: boolean;
|
|
232
|
+
xAxis: boolean;
|
|
233
|
+
yAxis: boolean;
|
|
234
|
+
locale: string;
|
|
235
|
+
currency: string;
|
|
236
|
+
yDomain: readonly [number, number] | null;
|
|
237
|
+
xDomain: readonly [number, number] | null;
|
|
238
|
+
table: boolean;
|
|
239
|
+
animate: boolean;
|
|
240
|
+
colors: readonly string[] | ReadonlyMap<string, string> | null;
|
|
241
|
+
colorBy: "sign" | {
|
|
242
|
+
readonly target: number;
|
|
243
|
+
} | string | null;
|
|
244
|
+
theme: Partial<Record<ThemeToken, string>>;
|
|
245
|
+
}
|
|
246
|
+
/** One (category, series) value after grouping/stacking. */
|
|
247
|
+
export interface Cell {
|
|
248
|
+
/** Category index into `Shaped.categories`. */
|
|
249
|
+
ci: number;
|
|
250
|
+
/** Series index into `Shaped.series` (0 when there is no series field). */
|
|
251
|
+
si: number;
|
|
252
|
+
/** Raw value, or null when the row is missing / null. */
|
|
253
|
+
value: number | null;
|
|
254
|
+
/** Bar/area span in data units. Grouped: [0, value]. Stacked: cumulative. */
|
|
255
|
+
y0: number;
|
|
256
|
+
y1: number;
|
|
257
|
+
}
|
|
258
|
+
export interface Shaped {
|
|
259
|
+
/** Category labels (String of the raw value; `OTHER` for the limit roll-up). */
|
|
260
|
+
categories: string[];
|
|
261
|
+
/** Waterfall: true where the category is in `spec.totals`. Parallel to `categories`. */
|
|
262
|
+
totals: boolean[];
|
|
263
|
+
/** Series keys in first-appearance order ([""] when there is no series field). */
|
|
264
|
+
series: string[];
|
|
265
|
+
/** Visible series only (excludes `view.hidden`), as indexes into `series`. */
|
|
266
|
+
visible: number[];
|
|
267
|
+
/** Cells for visible series, category-major order. */
|
|
268
|
+
cells: Cell[];
|
|
269
|
+
/** Min/max over all cell spans (includes 0 for bars). [0, 0] when empty. */
|
|
270
|
+
extent: [number, number];
|
|
271
|
+
/** spec.y2 aggregated per category (parallel to `categories`); [] without y2. */
|
|
272
|
+
y2: (number | null)[];
|
|
273
|
+
}
|
|
274
|
+
export interface BandScale {
|
|
275
|
+
domain: readonly string[];
|
|
276
|
+
/** Distance between band starts. */
|
|
277
|
+
step: number;
|
|
278
|
+
/** Width of one band. */
|
|
279
|
+
bandwidth: number;
|
|
280
|
+
/** Start coordinate of the band for `value`'s index. */
|
|
281
|
+
at(index: number): number;
|
|
282
|
+
}
|
|
283
|
+
export interface LinearScale {
|
|
284
|
+
domain: readonly [number, number];
|
|
285
|
+
range: readonly [number, number];
|
|
286
|
+
of(value: number): number;
|
|
287
|
+
}
|
|
288
|
+
/** Narrow with `"bandwidth" in s`. */
|
|
289
|
+
export type Scale = BandScale | LinearScale;
|
|
290
|
+
export interface Ticks {
|
|
291
|
+
/** Nice, step-aligned domain covering the input. */
|
|
292
|
+
domain: [number, number];
|
|
293
|
+
values: number[];
|
|
294
|
+
step: number;
|
|
295
|
+
}
|
|
296
|
+
export interface Box {
|
|
297
|
+
x: number;
|
|
298
|
+
y: number;
|
|
299
|
+
w: number;
|
|
300
|
+
h: number;
|
|
301
|
+
}
|
|
302
|
+
export interface Layout {
|
|
303
|
+
width: number;
|
|
304
|
+
height: number;
|
|
305
|
+
/** The plot rectangle (marks live here). */
|
|
306
|
+
plot: Box;
|
|
307
|
+
/** Draw every nth x label (1 = all). */
|
|
308
|
+
xLabelEvery: number;
|
|
309
|
+
}
|
|
310
|
+
/** One screen axis a mark asks for. `field` names the title/format source. */
|
|
311
|
+
export type Axis = {
|
|
312
|
+
kind: "band";
|
|
313
|
+
field: string;
|
|
314
|
+
domain: readonly string[];
|
|
315
|
+
} | {
|
|
316
|
+
kind: "linear";
|
|
317
|
+
field: string;
|
|
318
|
+
domain: readonly [number, number];
|
|
319
|
+
} | null;
|
|
320
|
+
export type Fail = (code: ErrorCode, path: string, headline: string, ...details: string[]) => never;
|
|
321
|
+
/** Where `ctx.label` places text relative to its anchor point. */
|
|
322
|
+
export type LabelPlace = "center" | "above" | "below" | "start" | "end";
|
|
323
|
+
/** Everything a mark may use. Closures are built in render.ts. */
|
|
324
|
+
export interface MarkCtx {
|
|
325
|
+
spec: ResolvedSpec;
|
|
326
|
+
shaped: Shaped;
|
|
327
|
+
width: number;
|
|
328
|
+
height: number;
|
|
329
|
+
plot: Box;
|
|
330
|
+
/** Scale of the bottom (horizontal) axis; null when the mark has none. */
|
|
331
|
+
x: Scale | null;
|
|
332
|
+
/** Scale of the left (vertical) axis; null when the mark has none. */
|
|
333
|
+
y: Scale | null;
|
|
334
|
+
/** Scale of the right axis (bar with y2); null otherwise. */
|
|
335
|
+
y2: LinearScale | null;
|
|
336
|
+
/** Display text for a raw value of `field` (step = tick step for number decimals). */
|
|
337
|
+
fmt(field: string, v: unknown, step?: number): string;
|
|
338
|
+
/** Queue a value label into `<g data-maya="labels">`; false if it collided and was dropped. */
|
|
339
|
+
label(x: number, y: number, text: string, place: LabelPlace): boolean;
|
|
340
|
+
/** colorBy tone for a value: "good" | "bad", or null when colorBy is not sign/target. */
|
|
341
|
+
tone(v: number): "good" | "bad" | null;
|
|
342
|
+
/** colorBy ramp bucket 0..9 for a value of the colorBy field; null when colorBy is not a field. */
|
|
343
|
+
q(v: number): number | null;
|
|
344
|
+
/** Shared aggregation (same rules as shape: nulls skipped, count = non-null). */
|
|
345
|
+
agg(kind: Aggregate): (values: readonly (number | null | undefined)[]) => number | null;
|
|
346
|
+
fail: Fail;
|
|
347
|
+
t(key: TextKey, ...args: (string | number)[]): string;
|
|
348
|
+
}
|
|
349
|
+
/** Markup for each SVG group a mark fills. Absent groups render empty. */
|
|
350
|
+
export interface MarkOut {
|
|
351
|
+
marks: string;
|
|
352
|
+
hits: string;
|
|
353
|
+
labels?: string;
|
|
354
|
+
legend?: string;
|
|
355
|
+
grid?: string;
|
|
356
|
+
cross?: string;
|
|
357
|
+
}
|
|
358
|
+
/** A chart type. Core marks live in render.ts's CORE map; modules `register()` theirs. */
|
|
359
|
+
export interface Mark {
|
|
360
|
+
/** Noun for the auto description ("Bar" -> "Bar chart of …"). */
|
|
361
|
+
noun: string;
|
|
362
|
+
/** Bottom and left axes, plus an optional right linear axis (bar with y2). Absent: no axes (path types, kpi). */
|
|
363
|
+
axes?(spec: ResolvedSpec, shaped: Shaped): [bottom: Axis, left: Axis, right?: Axis];
|
|
364
|
+
/** Extra validation after the core checks (e.g. hexmap `unknown-state`). */
|
|
365
|
+
check?(spec: ChartSpec, fail: Fail): void;
|
|
366
|
+
draw(ctx: MarkCtx): MarkOut;
|
|
367
|
+
}
|
|
368
|
+
/** Pieces the shell and the element assemble. */
|
|
369
|
+
export interface Parts {
|
|
370
|
+
/** `<svg>` markup without an embedded `<style>`. */
|
|
371
|
+
svg: string;
|
|
372
|
+
/** Legend HTML (`""` when hidden). */
|
|
373
|
+
legend: string;
|
|
374
|
+
/** Measure toggle HTML, a `.maya-ctl` radiogroup (`""` without a `y` array). */
|
|
375
|
+
controls: string;
|
|
376
|
+
/** Breadcrumb HTML, `.maya-crumbs` (`""` when not drilled). */
|
|
377
|
+
crumbs: string;
|
|
378
|
+
/** Hidden data table HTML (`""` when disabled). */
|
|
379
|
+
table: string;
|
|
380
|
+
/** Visible title HTML (`""` when no title). */
|
|
381
|
+
title: string;
|
|
382
|
+
/** Inline style for overrides from `spec.colors` / `spec.theme` (`""` when none). */
|
|
383
|
+
style: string;
|
|
384
|
+
/** The same overrides as [custom property, value] pairs (applied via CSSOM). */
|
|
385
|
+
vars: [string, string][];
|
|
386
|
+
/** Non-fatal notes for the developer (never rendered). */
|
|
387
|
+
warnings: string[];
|
|
388
|
+
}
|
|
389
|
+
/** Interaction state the element owns; rendered through RenderOptions.view/selected. */
|
|
390
|
+
export interface State {
|
|
391
|
+
view: View;
|
|
392
|
+
selected: readonly Sel[];
|
|
393
|
+
}
|
|
394
|
+
/** Sent to every reducer when the element's spec object changes (incl. `el.data =`). */
|
|
395
|
+
export interface SpecEvent {
|
|
396
|
+
type: "spec";
|
|
397
|
+
prev: ChartSpec | undefined;
|
|
398
|
+
next: ChartSpec;
|
|
399
|
+
}
|
|
400
|
+
/** What <maya-chart> hands each interaction module's `mount()`. */
|
|
401
|
+
export interface Host {
|
|
402
|
+
root: ShadowRoot;
|
|
403
|
+
el: HTMLElement;
|
|
404
|
+
spec(): ChartSpec | undefined;
|
|
405
|
+
state(): State;
|
|
406
|
+
/** User-initiated transition: re-render, then dispatch maya-view / maya-select if changed. */
|
|
407
|
+
commit(next: State, target?: (Sel & {
|
|
408
|
+
value: unknown;
|
|
409
|
+
}) | null): void;
|
|
410
|
+
/** Polite live-region announcement (debounced 300 ms). */
|
|
411
|
+
announce(text: string): void;
|
|
412
|
+
}
|
|
413
|
+
/** Returned by `mount()`. Keyboard handlers return true when they handled the key. */
|
|
414
|
+
export interface Handlers {
|
|
415
|
+
/** Abort an in-progress gesture (zoom brush). Escape priority 2. */
|
|
416
|
+
cancel?(): boolean;
|
|
417
|
+
/** Escape at this module's priority: select 3, zoom window 4, drill pop 5. */
|
|
418
|
+
escape?(): boolean;
|
|
419
|
+
/** Enter on the keyboard-active mark: drill first, then select. */
|
|
420
|
+
enter?(mark: Element): boolean;
|
|
421
|
+
/** After every paint (re-apply data-selected, restore focus). */
|
|
422
|
+
painted?(): void;
|
|
423
|
+
off(): void;
|
|
424
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { ChartSpec, ErrorCode, RenderOptions, ResolvedSpec, View } from "./types.ts";
|
|
2
|
+
export type { ErrorCode };
|
|
3
|
+
export declare class MayaSpecError extends Error {
|
|
4
|
+
readonly code: ErrorCode;
|
|
5
|
+
/** Spec path such as "y", "data[3].revenue", "theme.accent". */
|
|
6
|
+
readonly path: string;
|
|
7
|
+
constructor(code: ErrorCode, path: string, message: string);
|
|
8
|
+
}
|
|
9
|
+
export declare const MAX_MARKS = 5000;
|
|
10
|
+
/** Every spec key (schema.json is tested against this). */
|
|
11
|
+
export declare const KEYS: string[];
|
|
12
|
+
export declare const ONLY: Readonly<Record<string, readonly string[]>>;
|
|
13
|
+
/** Format options holding any date key are Intl.DateTimeFormat options. */
|
|
14
|
+
export declare const isDateOpts: (o: object) => boolean;
|
|
15
|
+
export declare const dym: (s: string, c: readonly string[]) => string;
|
|
16
|
+
export declare const fail: (code: ErrorCode, path: string, headline: string, ...d: string[]) => never;
|
|
17
|
+
export declare function validateSpec(spec: unknown): asserts spec is ChartSpec;
|
|
18
|
+
export declare function validateOptions(opts: unknown): asserts opts is RenderOptions;
|
|
19
|
+
/**
|
|
20
|
+
* Apply defaults plus the view's measure and drill. Assumes `spec` passed validateSpec.
|
|
21
|
+
* Drill keeps rows whose path[i] equals drill[i], then advances: bar/line/area take the next
|
|
22
|
+
* level as x; path types keep the remaining levels.
|
|
23
|
+
*/
|
|
24
|
+
export declare function resolve(spec: ChartSpec, view?: View): ResolvedSpec;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/** x, y, width, height in viewBox units. */
|
|
2
|
+
export type Box = [x: number, y: number, w: number, h: number];
|
|
3
|
+
export interface PatchOptions {
|
|
4
|
+
/** Entering marks start from this box; exiting marks collapse into it. */
|
|
5
|
+
origin?: Box | undefined;
|
|
6
|
+
/** Post-patch hook, called with the live svg after every patch (animated or not). */
|
|
7
|
+
after?: (svg: Element) => void;
|
|
8
|
+
/** Diff and reuse nodes, but apply every change immediately (resize). */
|
|
9
|
+
instant?: boolean;
|
|
10
|
+
}
|
|
11
|
+
/** Box of any mark (paths via getBBox where it exists); used to derive a drill origin. */
|
|
12
|
+
export declare const boxOf: (e: Element) => Box | undefined;
|
|
13
|
+
export declare function patch(box: Element, markup: string, animate: boolean, opts?: PatchOptions): void;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { ChartSpec, Handlers, Host, SpecEvent, State } from "../core/types.ts";
|
|
2
|
+
/** `spec` is needed to gate a push (the reducer has no other access to it). */
|
|
3
|
+
export type DrillEvent = {
|
|
4
|
+
type: "drill";
|
|
5
|
+
value: string;
|
|
6
|
+
spec?: ChartSpec;
|
|
7
|
+
} | {
|
|
8
|
+
type: "pop";
|
|
9
|
+
depth: number;
|
|
10
|
+
} | SpecEvent;
|
|
11
|
+
export declare const reduce: (s: State, e: DrillEvent) => State;
|
|
12
|
+
export declare const mount: (host: Host) => Handlers;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const html: (s: string) => string;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { ChartSpec, Sel, View } from "../core/types.ts";
|
|
2
|
+
export declare class MayaChart extends HTMLElement {
|
|
3
|
+
#private;
|
|
4
|
+
static observedAttributes: string[];
|
|
5
|
+
static version: string;
|
|
6
|
+
get spec(): ChartSpec | undefined;
|
|
7
|
+
set spec(v: ChartSpec<any> | undefined);
|
|
8
|
+
get data(): ChartSpec["data"] | undefined;
|
|
9
|
+
set data(rows: ChartSpec["data"]);
|
|
10
|
+
/** Interaction state ({ measure, drill, window, hidden }). Setting never dispatches events. */
|
|
11
|
+
get view(): View;
|
|
12
|
+
set view(v: View);
|
|
13
|
+
/** Selected marks by raw values. Setting never dispatches events. */
|
|
14
|
+
get selected(): readonly Sel[];
|
|
15
|
+
set selected(v: readonly Sel[]);
|
|
16
|
+
attributeChangedCallback(_: string, __: string | null, v: string | null): void;
|
|
17
|
+
connectedCallback(): void;
|
|
18
|
+
disconnectedCallback(): void;
|
|
19
|
+
/** Standalone SVG: custom properties resolved from the computed style (dark export looks right). */
|
|
20
|
+
toSVG(): string;
|
|
21
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { Handlers, Host, SpecEvent, State } from "../core/types.ts";
|
|
2
|
+
/** `count` (number of measures) bounds the index; without it only the lower bound applies. */
|
|
3
|
+
export type MeasureEvent = {
|
|
4
|
+
type: "measure" | "pick";
|
|
5
|
+
index: number;
|
|
6
|
+
count?: number | undefined;
|
|
7
|
+
} | SpecEvent;
|
|
8
|
+
export declare const reduce: (s: State, e: MeasureEvent) => State;
|
|
9
|
+
export declare const mount: (host: Host) => Handlers;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { Handlers, Host, Sel, SpecEvent, State } from "../core/types.ts";
|
|
2
|
+
export type SelectEvent = {
|
|
3
|
+
type: "toggle";
|
|
4
|
+
sel: Sel;
|
|
5
|
+
multi: boolean;
|
|
6
|
+
} | {
|
|
7
|
+
type: "clear";
|
|
8
|
+
} | SpecEvent;
|
|
9
|
+
export declare function reduce(s: State, e: SelectEvent): State;
|
|
10
|
+
/** Raw-value Sel of a mark, decoded from its data-key (`S~C`, scatter `S~name`, `h~…`). */
|
|
11
|
+
export declare function selOf(mark: Element, type?: string): Sel | undefined;
|
|
12
|
+
/** A Sel matches a mark when every field it names equals the mark's (compared as text). */
|
|
13
|
+
export declare function matches(sel: Sel, mark: Element, type?: string): boolean;
|
|
14
|
+
export declare function mount(host: Host): Handlers;
|