@diagrammo/dgmo 0.47.0 → 0.48.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/dist/auto.js +1 -1
- package/dist/auto.mjs +1 -1
- package/dist/element.js +1 -1
- package/dist/element.mjs +1 -1
- package/package.json +19 -70
- package/dist/advanced.cjs +0 -71648
- package/dist/advanced.d.cts +0 -5719
- package/dist/auto.cjs +0 -70648
- package/dist/auto.d.cts +0 -54
- package/dist/block.cjs +0 -70208
- package/dist/block.d.cts +0 -115
- package/dist/editor.cjs +0 -526
- package/dist/editor.d.cts +0 -26
- package/dist/element.cjs +0 -70530
- package/dist/element.d.cts +0 -62
- package/dist/highlight.cjs +0 -852
- package/dist/highlight.d.cts +0 -49
- package/dist/index.cjs +0 -70491
- package/dist/index.d.cts +0 -160
- package/dist/pert.cjs +0 -325
- package/dist/pert.d.cts +0 -559
- package/dist/themes-1-CuKpeH.d.cts +0 -908
- package/src/advanced.ts +0 -873
- package/src/arc/parser.ts +0 -15
- package/src/arc/renderer.ts +0 -633
- package/src/auto/index.ts +0 -621
- package/src/auto/shared.ts +0 -370
- package/src/auto/styles.ts +0 -73
- package/src/block/layout.ts +0 -198
- package/src/block/parser.ts +0 -427
- package/src/block/renderer.ts +0 -484
- package/src/block/types.ts +0 -67
- package/src/boxes-and-lines/collapse.ts +0 -96
- package/src/boxes-and-lines/focus.ts +0 -257
- package/src/boxes-and-lines/label-placement.ts +0 -339
- package/src/boxes-and-lines/layout-grouped.ts +0 -912
- package/src/boxes-and-lines/layout-layered.ts +0 -722
- package/src/boxes-and-lines/layout-search.ts +0 -1678
- package/src/boxes-and-lines/layout.ts +0 -398
- package/src/boxes-and-lines/parser.ts +0 -1181
- package/src/boxes-and-lines/renderer.ts +0 -1465
- package/src/boxes-and-lines/types.ts +0 -66
- package/src/c4/layout.ts +0 -2441
- package/src/c4/parser.ts +0 -1158
- package/src/c4/renderer.ts +0 -2008
- package/src/c4/types.ts +0 -89
- package/src/chart-type-registry.ts +0 -529
- package/src/chart-types.ts +0 -225
- package/src/chart.ts +0 -685
- package/src/charts-d3/bar.ts +0 -338
- package/src/charts-d3/chord.ts +0 -140
- package/src/charts-d3/function.ts +0 -147
- package/src/charts-d3/funnel.ts +0 -104
- package/src/charts-d3/heatmap.ts +0 -145
- package/src/charts-d3/index.ts +0 -337
- package/src/charts-d3/interactions.ts +0 -477
- package/src/charts-d3/line.ts +0 -344
- package/src/charts-d3/mount.ts +0 -151
- package/src/charts-d3/pie.ts +0 -117
- package/src/charts-d3/polar.ts +0 -89
- package/src/charts-d3/radar.ts +0 -0
- package/src/charts-d3/sankey.ts +0 -0
- package/src/charts-d3/scatter.ts +0 -185
- package/src/charts-d3/shared.ts +0 -208
- package/src/class/layout.ts +0 -326
- package/src/class/parser.ts +0 -635
- package/src/class/renderer.ts +0 -793
- package/src/class/types.ts +0 -58
- package/src/cli-banner.ts +0 -106
- package/src/cli.ts +0 -1080
- package/src/colors-diagnostics.ts +0 -35
- package/src/colors.ts +0 -379
- package/src/completion-types.ts +0 -27
- package/src/completion.ts +0 -2668
- package/src/cycle/layout.ts +0 -1026
- package/src/cycle/parser.ts +0 -387
- package/src/cycle/renderer.ts +0 -653
- package/src/cycle/types.ts +0 -101
- package/src/d3.ts +0 -1434
- package/src/data-chart-parser.ts +0 -1007
- package/src/dgmo-router.ts +0 -296
- package/src/diagnostics-registry.ts +0 -108
- package/src/diagnostics.ts +0 -0
- package/src/dimensions.ts +0 -20
- package/src/directives-registry.ts +0 -396
- package/src/editor/dgmo.grammar +0 -76
- package/src/editor/dgmo.grammar.d.ts +0 -2
- package/src/editor/dgmo.grammar.js +0 -18
- package/src/editor/dgmo.grammar.terms.d.ts +0 -5
- package/src/editor/dgmo.grammar.terms.js +0 -35
- package/src/editor/highlight-api.ts +0 -596
- package/src/editor/highlight.ts +0 -36
- package/src/editor/index.ts +0 -27
- package/src/editor/keywords.ts +0 -128
- package/src/editor/tokens.ts +0 -30
- package/src/element/index.ts +0 -432
- package/src/embed/css.ts +0 -312
- package/src/embed/escape.ts +0 -15
- package/src/embed/index.ts +0 -280
- package/src/er/classify.ts +0 -228
- package/src/er/layout.ts +0 -530
- package/src/er/parser.ts +0 -750
- package/src/er/renderer.ts +0 -725
- package/src/er/types.ts +0 -52
- package/src/error-card.ts +0 -269
- package/src/event-line/diagnostics.ts +0 -117
- package/src/event-line/parser.ts +0 -643
- package/src/event-line/renderer.ts +0 -1742
- package/src/event-line/types.ts +0 -74
- package/src/fonts.ts +0 -3
- package/src/gantt/calculator.ts +0 -979
- package/src/gantt/parser.ts +0 -1641
- package/src/gantt/renderer.ts +0 -3787
- package/src/gantt/resolver.ts +0 -172
- package/src/gantt/types.ts +0 -206
- package/src/graph/diagnostics.ts +0 -64
- package/src/graph/edge-spline.ts +0 -29
- package/src/graph/flowchart-parser.ts +0 -759
- package/src/graph/flowchart-renderer.ts +0 -817
- package/src/graph/layout.ts +0 -472
- package/src/graph/notes.ts +0 -21
- package/src/graph/state-collapse.ts +0 -78
- package/src/graph/state-parser.ts +0 -574
- package/src/graph/state-renderer.ts +0 -653
- package/src/graph/types.ts +0 -65
- package/src/index.ts +0 -261
- package/src/infra/compute.ts +0 -1385
- package/src/infra/layout.ts +0 -888
- package/src/infra/parser.ts +0 -1234
- package/src/infra/renderer.ts +0 -2534
- package/src/infra/roles.ts +0 -75
- package/src/infra/types.ts +0 -216
- package/src/infra/validation.ts +0 -201
- package/src/journey-map/layout.ts +0 -490
- package/src/journey-map/parser.ts +0 -557
- package/src/journey-map/renderer.ts +0 -1580
- package/src/journey-map/types.ts +0 -47
- package/src/kanban/mutations.ts +0 -175
- package/src/kanban/parser.ts +0 -509
- package/src/kanban/renderer.ts +0 -1449
- package/src/kanban/types.ts +0 -39
- package/src/label-layout.ts +0 -368
- package/src/map/colorize.ts +0 -54
- package/src/map/completion.ts +0 -316
- package/src/map/context-labels.ts +0 -609
- package/src/map/data/PROVENANCE.json +0 -1
- package/src/map/data/README.md +0 -40
- package/src/map/data/airport-collisions.json +0 -1
- package/src/map/data/airports.json +0 -1
- package/src/map/data/gazetteer.json +0 -1
- package/src/map/data/lakes.json +0 -1
- package/src/map/data/mountain-ranges.json +0 -1
- package/src/map/data/na-lakes.json +0 -1
- package/src/map/data/na-land.json +0 -1
- package/src/map/data/region-names.json +0 -1
- package/src/map/data/rivers.json +0 -1
- package/src/map/data/types.ts +0 -121
- package/src/map/data/us-states.json +0 -1
- package/src/map/data/water-bodies.json +0 -1
- package/src/map/data/world-coarse.json +0 -1
- package/src/map/data/world-detail.json +0 -1
- package/src/map/diagnostics.ts +0 -134
- package/src/map/dimensions.ts +0 -150
- package/src/map/geo-query.ts +0 -295
- package/src/map/geo.ts +0 -438
- package/src/map/invert.ts +0 -111
- package/src/map/layout.ts +0 -5069
- package/src/map/legend-band.ts +0 -127
- package/src/map/load-data.ts +0 -186
- package/src/map/parser.ts +0 -783
- package/src/map/renderer.ts +0 -1445
- package/src/map/resolved-types.ts +0 -160
- package/src/map/resolver.ts +0 -1146
- package/src/map/types.ts +0 -194
- package/src/mindmap/collapse.ts +0 -67
- package/src/mindmap/layout.ts +0 -649
- package/src/mindmap/parser.ts +0 -430
- package/src/mindmap/renderer.ts +0 -637
- package/src/mindmap/text-wrap.ts +0 -218
- package/src/mindmap/types.ts +0 -56
- package/src/org/collapse.ts +0 -151
- package/src/org/layout.ts +0 -1262
- package/src/org/parser.ts +0 -519
- package/src/org/renderer.ts +0 -866
- package/src/org/resolver.ts +0 -636
- package/src/palettes/atlas.ts +0 -77
- package/src/palettes/blueprint.ts +0 -73
- package/src/palettes/catppuccin.ts +0 -84
- package/src/palettes/color-utils.ts +0 -443
- package/src/palettes/index.ts +0 -67
- package/src/palettes/nord.ts +0 -67
- package/src/palettes/registry.ts +0 -114
- package/src/palettes/slate.ts +0 -73
- package/src/palettes/tidewater.ts +0 -73
- package/src/palettes/tokyo-night.ts +0 -86
- package/src/palettes/types.ts +0 -88
- package/src/pert/analyzer.ts +0 -1183
- package/src/pert/diagnostics.ts +0 -110
- package/src/pert/index.ts +0 -43
- package/src/pert/internal.ts +0 -252
- package/src/pert/layout.ts +0 -971
- package/src/pert/monte-carlo.ts +0 -446
- package/src/pert/parser.ts +0 -1606
- package/src/pert/renderer.ts +0 -4009
- package/src/pert/share-normalize.ts +0 -62
- package/src/pert/types.ts +0 -604
- package/src/pyramid/parser.ts +0 -185
- package/src/pyramid/renderer.ts +0 -768
- package/src/pyramid/types.ts +0 -28
- package/src/quadrant/parser.ts +0 -18
- package/src/quadrant/renderer.ts +0 -638
- package/src/raci/diagnostics.ts +0 -213
- package/src/raci/index.ts +0 -23
- package/src/raci/mutations.ts +0 -321
- package/src/raci/parser.ts +0 -699
- package/src/raci/renderer.ts +0 -1627
- package/src/raci/types.ts +0 -82
- package/src/raci/variants.ts +0 -320
- package/src/render.ts +0 -268
- package/src/ring/parser.ts +0 -212
- package/src/ring/renderer.ts +0 -490
- package/src/ring/types.ts +0 -27
- package/src/sequence/collapse.ts +0 -174
- package/src/sequence/parser.ts +0 -1596
- package/src/sequence/participant-inference.ts +0 -152
- package/src/sequence/renderer.ts +0 -3168
- package/src/sequence/tag-resolution.ts +0 -164
- package/src/sharing.ts +0 -211
- package/src/sitemap/collapse.ts +0 -100
- package/src/sitemap/layout.ts +0 -925
- package/src/sitemap/parser.ts +0 -755
- package/src/sitemap/renderer.ts +0 -828
- package/src/sitemap/types.ts +0 -42
- package/src/slope/parser.ts +0 -18
- package/src/slope/renderer.ts +0 -378
- package/src/swimlane/diagnostics.ts +0 -94
- package/src/swimlane/layout.ts +0 -597
- package/src/swimlane/parser.ts +0 -613
- package/src/swimlane/renderer.ts +0 -681
- package/src/swimlane/types.ts +0 -151
- package/src/tech-radar/interactive.ts +0 -1129
- package/src/tech-radar/layout.ts +0 -197
- package/src/tech-radar/parser.ts +0 -436
- package/src/tech-radar/renderer.ts +0 -1239
- package/src/tech-radar/shared.ts +0 -151
- package/src/tech-radar/types.ts +0 -86
- package/src/themes.ts +0 -22
- package/src/timeline/parser.ts +0 -472
- package/src/timeline/renderer.ts +0 -3024
- package/src/timeline/types.ts +0 -33
- package/src/timeline/viz-parser.ts +0 -21
- package/src/treemap/diagnostics.ts +0 -62
- package/src/treemap/layout-radial.ts +0 -233
- package/src/treemap/layout.ts +0 -189
- package/src/treemap/parser.ts +0 -463
- package/src/treemap/renderer-radial.ts +0 -438
- package/src/treemap/renderer.ts +0 -572
- package/src/treemap/treemap-shared.ts +0 -309
- package/src/treemap/types.ts +0 -60
- package/src/utils/arrow-markers.ts +0 -51
- package/src/utils/arrows-diagnostics.ts +0 -62
- package/src/utils/arrows.ts +0 -227
- package/src/utils/brand.ts +0 -61
- package/src/utils/card.ts +0 -183
- package/src/utils/collapse-engine/tree-edges.ts +0 -115
- package/src/utils/collapse-engine/tree.ts +0 -117
- package/src/utils/d3-helpers.ts +0 -219
- package/src/utils/d3-types.ts +0 -9
- package/src/utils/description-helpers.ts +0 -38
- package/src/utils/duration.ts +0 -312
- package/src/utils/export-container.ts +0 -41
- package/src/utils/expr-eval.ts +0 -27
- package/src/utils/fit-canvas.ts +0 -64
- package/src/utils/hover-styles.ts +0 -673
- package/src/utils/inline-markdown.ts +0 -96
- package/src/utils/legend-constants.ts +0 -48
- package/src/utils/legend-d3.ts +0 -618
- package/src/utils/legend-integration.ts +0 -107
- package/src/utils/legend-layout.ts +0 -776
- package/src/utils/legend-svg.ts +0 -214
- package/src/utils/legend-types.ts +0 -272
- package/src/utils/name-normalize.ts +0 -163
- package/src/utils/note-box/constants.ts +0 -25
- package/src/utils/note-box/index.ts +0 -11
- package/src/utils/note-box/metrics.ts +0 -90
- package/src/utils/note-box/svg.ts +0 -331
- package/src/utils/notes/bounds.ts +0 -30
- package/src/utils/notes/build.ts +0 -131
- package/src/utils/notes/index.ts +0 -18
- package/src/utils/notes/model.ts +0 -19
- package/src/utils/notes/parse.ts +0 -131
- package/src/utils/notes/place.ts +0 -177
- package/src/utils/notes/resolve.ts +0 -88
- package/src/utils/number-format.ts +0 -36
- package/src/utils/parsing.ts +0 -953
- package/src/utils/reserved-key-registry.ts +0 -239
- package/src/utils/safe-href.ts +0 -38
- package/src/utils/scaling.ts +0 -97
- package/src/utils/svg-embed.ts +0 -251
- package/src/utils/tag-groups.ts +0 -607
- package/src/utils/text-measure.ts +0 -122
- package/src/utils/time-ticks.ts +0 -253
- package/src/utils/title-constants.ts +0 -24
- package/src/utils/visual-conventions.ts +0 -61
- package/src/utils/wrapped-desc.ts +0 -87
- package/src/venn/parser.ts +0 -18
- package/src/venn/renderer.ts +0 -1047
- package/src/version-control/diagnostics.ts +0 -67
- package/src/version-control/parser.ts +0 -544
- package/src/version-control/renderer.ts +0 -724
- package/src/version-control/types.ts +0 -100
- package/src/visualizations/parse.ts +0 -1560
- package/src/visualizations/types.ts +0 -262
- package/src/wireframe/layout.ts +0 -479
- package/src/wireframe/parser.ts +0 -1114
- package/src/wireframe/renderer.ts +0 -1318
- package/src/wireframe/types.ts +0 -110
- package/src/wordcloud/parser.ts +0 -18
- package/src/wordcloud/renderer.ts +0 -357
package/src/charts-d3/line.ts
DELETED
|
@@ -1,344 +0,0 @@
|
|
|
1
|
-
// ============================================================
|
|
2
|
-
// Hand-built line / multi-line / area renderer.
|
|
3
|
-
// Series-tinted value labels, 0-baseline, era bands (markArea parity),
|
|
4
|
-
// dual y-axis (§15.1: y-label / y-right-label grouped series).
|
|
5
|
-
// ============================================================
|
|
6
|
-
|
|
7
|
-
import { scalePoint, scaleLinear, type ScaleLinear } from 'd3-scale';
|
|
8
|
-
import { line as d3line, area as d3area } from 'd3-shape';
|
|
9
|
-
import type { ParsedChart } from '../chart';
|
|
10
|
-
import type { PaletteColors } from '../palettes';
|
|
11
|
-
import { FONT_FAMILY } from '../fonts';
|
|
12
|
-
import { mix, shapeFill } from '../palettes/color-utils';
|
|
13
|
-
import {
|
|
14
|
-
type Svg,
|
|
15
|
-
type Margins,
|
|
16
|
-
TICK_FONT,
|
|
17
|
-
fmtNum,
|
|
18
|
-
reserveHeader,
|
|
19
|
-
computeLeftMargin,
|
|
20
|
-
drawXAxisTitle,
|
|
21
|
-
drawYAxisTitle,
|
|
22
|
-
drawValueLabel,
|
|
23
|
-
} from './shared';
|
|
24
|
-
|
|
25
|
-
function seriesValue(
|
|
26
|
-
pt: { value: number; extraValues?: number[] },
|
|
27
|
-
s: number
|
|
28
|
-
): number {
|
|
29
|
-
return s === 0 ? pt.value : (pt.extraValues?.[s - 1] ?? 0);
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
export function renderLine(
|
|
33
|
-
svg: Svg,
|
|
34
|
-
chart: ParsedChart,
|
|
35
|
-
width: number,
|
|
36
|
-
height: number,
|
|
37
|
-
colors: string[],
|
|
38
|
-
palette: PaletteColors,
|
|
39
|
-
isDark: boolean,
|
|
40
|
-
textColor: string,
|
|
41
|
-
mutedColor: string,
|
|
42
|
-
bgColor: string,
|
|
43
|
-
hasTitle: boolean
|
|
44
|
-
): void {
|
|
45
|
-
const data = chart.data;
|
|
46
|
-
// `line` + a `fill` directive → filled (area) rendering. (#25)
|
|
47
|
-
const isArea = chart.fill === true;
|
|
48
|
-
const seriesNames = chart.seriesNames?.length
|
|
49
|
-
? chart.seriesNames
|
|
50
|
-
: [chart.series ?? ''];
|
|
51
|
-
const seriesCount = Math.max(
|
|
52
|
-
1,
|
|
53
|
-
seriesNames.length,
|
|
54
|
-
1 + Math.max(0, ...data.map((d) => d.extraValues?.length ?? 0))
|
|
55
|
-
);
|
|
56
|
-
const seriesColor = (s: number): string =>
|
|
57
|
-
chart.seriesNameColors?.[s] ?? colors[s % colors.length]!;
|
|
58
|
-
|
|
59
|
-
// Dual-axis (§15.1): per-series left/right assignment; absent ⇒ all left.
|
|
60
|
-
const axisOf = (s: number): 'left' | 'right' =>
|
|
61
|
-
chart.seriesAxes?.[s] ?? 'left';
|
|
62
|
-
const dual = (chart.seriesAxes ?? []).includes('right');
|
|
63
|
-
// An axis owned by exactly one series adopts that series' color.
|
|
64
|
-
const soleAxisColor = (side: 'left' | 'right'): string | undefined => {
|
|
65
|
-
const owners: number[] = [];
|
|
66
|
-
for (let s = 0; s < seriesCount; s++)
|
|
67
|
-
if (axisOf(s) === side) owners.push(s);
|
|
68
|
-
return owners.length === 1 ? seriesColor(owners[0]!) : undefined;
|
|
69
|
-
};
|
|
70
|
-
|
|
71
|
-
const top = reserveHeader(
|
|
72
|
-
svg,
|
|
73
|
-
chart,
|
|
74
|
-
colors,
|
|
75
|
-
palette,
|
|
76
|
-
isDark,
|
|
77
|
-
hasTitle,
|
|
78
|
-
width
|
|
79
|
-
);
|
|
80
|
-
|
|
81
|
-
// Per-axis value extent (0-baseline for all-positive data, ECharts parity).
|
|
82
|
-
const extentFor = (side: 'left' | 'right'): [number, number] => {
|
|
83
|
-
let lo = Infinity;
|
|
84
|
-
let hi = -Infinity;
|
|
85
|
-
for (const d of data)
|
|
86
|
-
for (let s = 0; s < seriesCount; s++)
|
|
87
|
-
if (axisOf(s) === side) {
|
|
88
|
-
const v = seriesValue(d, s);
|
|
89
|
-
lo = Math.min(lo, v);
|
|
90
|
-
hi = Math.max(hi, v);
|
|
91
|
-
}
|
|
92
|
-
if (!isFinite(lo)) return [0, 1];
|
|
93
|
-
if (lo > 0) lo = 0;
|
|
94
|
-
if (lo === hi) hi = lo + 1;
|
|
95
|
-
return [lo, hi];
|
|
96
|
-
};
|
|
97
|
-
const [loL, hiL] = extentFor('left');
|
|
98
|
-
const [loR, hiR] = dual ? extentFor('right') : [0, 1];
|
|
99
|
-
|
|
100
|
-
const m: Margins = {
|
|
101
|
-
top: top + 8,
|
|
102
|
-
right: dual
|
|
103
|
-
? computeLeftMargin(chart.yrlabel, [fmtNum(hiR), fmtNum(loR)])
|
|
104
|
-
: 32,
|
|
105
|
-
bottom: 64,
|
|
106
|
-
left: computeLeftMargin(chart.ylabel, [fmtNum(hiL), fmtNum(loL)]),
|
|
107
|
-
};
|
|
108
|
-
const plotW = width - m.left - m.right;
|
|
109
|
-
const plotH = height - m.top - m.bottom;
|
|
110
|
-
|
|
111
|
-
const x = scalePoint<string>()
|
|
112
|
-
.domain(data.map((d) => d.label))
|
|
113
|
-
.range([m.left, m.left + plotW])
|
|
114
|
-
.padding(0.5);
|
|
115
|
-
const yL = scaleLinear()
|
|
116
|
-
.domain([loL, hiL])
|
|
117
|
-
.nice()
|
|
118
|
-
.range([m.top + plotH, m.top]);
|
|
119
|
-
const yR: ScaleLinear<number, number> | null = dual
|
|
120
|
-
? scaleLinear()
|
|
121
|
-
.domain([loR, hiR])
|
|
122
|
-
.nice()
|
|
123
|
-
.range([m.top + plotH, m.top])
|
|
124
|
-
: null;
|
|
125
|
-
const yFor = (s: number): ScaleLinear<number, number> =>
|
|
126
|
-
dual && axisOf(s) === 'right' && yR ? yR : yL;
|
|
127
|
-
const leftTint = dual ? soleAxisColor('left') : undefined;
|
|
128
|
-
const rightTint = soleAxisColor('right');
|
|
129
|
-
|
|
130
|
-
// era bands (markArea parity) — positioned on the left axis
|
|
131
|
-
if (chart.eras?.length) {
|
|
132
|
-
chart.eras.forEach((era, i) => {
|
|
133
|
-
const xs = x(era.start);
|
|
134
|
-
const xe = x(era.end);
|
|
135
|
-
if (xs == null || xe == null) return;
|
|
136
|
-
const fill = era.color ?? colors[i % colors.length]!;
|
|
137
|
-
svg
|
|
138
|
-
.append('rect')
|
|
139
|
-
.attr('x', Math.min(xs, xe))
|
|
140
|
-
.attr('y', m.top)
|
|
141
|
-
.attr('width', Math.abs(xe - xs))
|
|
142
|
-
.attr('height', plotH)
|
|
143
|
-
.attr('fill', mix(fill, bgColor, 12));
|
|
144
|
-
svg
|
|
145
|
-
.append('text')
|
|
146
|
-
.attr('x', (xs + xe) / 2)
|
|
147
|
-
.attr('y', m.top + 14)
|
|
148
|
-
.attr('text-anchor', 'middle')
|
|
149
|
-
.attr('fill', textColor)
|
|
150
|
-
.attr('font-size', TICK_FONT)
|
|
151
|
-
.attr('font-family', FONT_FAMILY)
|
|
152
|
-
.attr('opacity', 0.8)
|
|
153
|
-
.text(era.label);
|
|
154
|
-
});
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
// left y gridlines + ticks (only the left axis draws gridlines)
|
|
158
|
-
for (const t of yL.ticks(6)) {
|
|
159
|
-
const yy = yL(t);
|
|
160
|
-
svg
|
|
161
|
-
.append('line')
|
|
162
|
-
.attr('x1', m.left)
|
|
163
|
-
.attr('x2', m.left + plotW)
|
|
164
|
-
.attr('y1', yy)
|
|
165
|
-
.attr('y2', yy)
|
|
166
|
-
.attr('stroke', mutedColor)
|
|
167
|
-
.attr('stroke-opacity', t === 0 ? 0.6 : 0.25);
|
|
168
|
-
svg
|
|
169
|
-
.append('text')
|
|
170
|
-
.attr('class', 'dgmo-tick')
|
|
171
|
-
.attr('x', m.left - 10)
|
|
172
|
-
.attr('y', yy + 4)
|
|
173
|
-
.attr('text-anchor', 'end')
|
|
174
|
-
.attr('fill', leftTint ?? textColor)
|
|
175
|
-
.attr('font-size', TICK_FONT)
|
|
176
|
-
.attr('font-family', FONT_FAMILY)
|
|
177
|
-
.text(fmtNum(t));
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
// right y ticks (no gridlines)
|
|
181
|
-
if (dual && yR) {
|
|
182
|
-
for (const t of yR.ticks(6)) {
|
|
183
|
-
svg
|
|
184
|
-
.append('text')
|
|
185
|
-
.attr('class', 'dgmo-tick')
|
|
186
|
-
.attr('x', m.left + plotW + 10)
|
|
187
|
-
.attr('y', yR(t) + 4)
|
|
188
|
-
.attr('text-anchor', 'start')
|
|
189
|
-
.attr('fill', rightTint ?? textColor)
|
|
190
|
-
.attr('font-size', TICK_FONT)
|
|
191
|
-
.attr('font-family', FONT_FAMILY)
|
|
192
|
-
.text(fmtNum(t));
|
|
193
|
-
}
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
// x category labels
|
|
197
|
-
for (const d of data) {
|
|
198
|
-
svg
|
|
199
|
-
.append('text')
|
|
200
|
-
.attr('class', 'dgmo-tick')
|
|
201
|
-
.attr('x', x(d.label) ?? 0)
|
|
202
|
-
.attr('y', m.top + plotH + 18)
|
|
203
|
-
.attr('text-anchor', 'middle')
|
|
204
|
-
.attr('fill', textColor)
|
|
205
|
-
.attr('font-size', TICK_FONT)
|
|
206
|
-
.attr('font-family', FONT_FAMILY)
|
|
207
|
-
.text(d.label);
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
// Transparent hit-target over the plot area — the interaction adapter reads
|
|
211
|
-
// its bounds for crosshair extent and snapping (see charts-d3/interactions.ts).
|
|
212
|
-
svg
|
|
213
|
-
.append('rect')
|
|
214
|
-
.attr('class', 'dgmo-plot-rect')
|
|
215
|
-
.attr('x', m.left)
|
|
216
|
-
.attr('y', m.top)
|
|
217
|
-
.attr('width', plotW)
|
|
218
|
-
.attr('height', plotH)
|
|
219
|
-
.attr('fill', 'transparent');
|
|
220
|
-
|
|
221
|
-
for (let s = 0; s < seriesCount; s++) {
|
|
222
|
-
const color = seriesColor(s);
|
|
223
|
-
const yy = yFor(s);
|
|
224
|
-
const name = seriesNames[s] ?? `Series ${s + 1}`;
|
|
225
|
-
const pts = data.map((d, i) => ({
|
|
226
|
-
label: d.label,
|
|
227
|
-
v: seriesValue(d, s),
|
|
228
|
-
xIndex: i,
|
|
229
|
-
lineNumber: d.lineNumber,
|
|
230
|
-
}));
|
|
231
|
-
const g = svg
|
|
232
|
-
.append('g')
|
|
233
|
-
.attr('class', 'dgmo-series')
|
|
234
|
-
.attr('data-series-index', s)
|
|
235
|
-
.attr('data-series-name', name)
|
|
236
|
-
.attr('data-axis', axisOf(s))
|
|
237
|
-
.attr('data-color', color);
|
|
238
|
-
const seriesLine = chart.seriesNameLineNumbers?.[s];
|
|
239
|
-
if (seriesLine !== undefined) g.attr('data-line-number', seriesLine);
|
|
240
|
-
|
|
241
|
-
if (isArea) {
|
|
242
|
-
const areaGen = d3area<(typeof pts)[number]>()
|
|
243
|
-
.x((p) => x(p.label) ?? 0)
|
|
244
|
-
.y0(yy(Math.max(yy.domain()[0]!, 0)))
|
|
245
|
-
.y1((p) => yy(p.v));
|
|
246
|
-
g.append('path')
|
|
247
|
-
.attr('class', 'dgmo-series-area')
|
|
248
|
-
.attr('d', areaGen(pts) ?? '')
|
|
249
|
-
.attr('fill', shapeFill(palette, color, isDark))
|
|
250
|
-
.attr('stroke', 'none');
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
const lineGen = d3line<(typeof pts)[number]>()
|
|
254
|
-
.x((p) => x(p.label) ?? 0)
|
|
255
|
-
.y((p) => yy(p.v));
|
|
256
|
-
g.append('path')
|
|
257
|
-
.attr('class', 'dgmo-series-line')
|
|
258
|
-
.attr('d', lineGen(pts) ?? '')
|
|
259
|
-
.attr('fill', 'none')
|
|
260
|
-
.attr('stroke', color)
|
|
261
|
-
.attr('stroke-width', 2.5)
|
|
262
|
-
.attr('stroke-linejoin', 'round')
|
|
263
|
-
.attr('stroke-linecap', 'round');
|
|
264
|
-
|
|
265
|
-
const labelColor = mix(color, textColor, 60);
|
|
266
|
-
for (const p of pts) {
|
|
267
|
-
g.append('circle')
|
|
268
|
-
.attr('class', 'dgmo-pt')
|
|
269
|
-
.attr('data-series-index', s)
|
|
270
|
-
.attr('data-series-name', name)
|
|
271
|
-
.attr('data-color', color)
|
|
272
|
-
.attr('data-x-index', p.xIndex)
|
|
273
|
-
.attr('data-x-label', p.label)
|
|
274
|
-
.attr('data-value', p.v)
|
|
275
|
-
.attr('data-line-number', p.lineNumber)
|
|
276
|
-
.attr('cx', x(p.label) ?? 0)
|
|
277
|
-
.attr('cy', yy(p.v))
|
|
278
|
-
.attr('r', 3.5)
|
|
279
|
-
.attr('fill', bgColor)
|
|
280
|
-
.attr('stroke', color)
|
|
281
|
-
.attr('stroke-width', 2);
|
|
282
|
-
if (!chart.noValue) {
|
|
283
|
-
drawValueLabel(
|
|
284
|
-
svg,
|
|
285
|
-
fmtNum(p.v),
|
|
286
|
-
x(p.label) ?? 0,
|
|
287
|
-
yy(p.v) - 10,
|
|
288
|
-
labelColor
|
|
289
|
-
);
|
|
290
|
-
}
|
|
291
|
-
}
|
|
292
|
-
}
|
|
293
|
-
|
|
294
|
-
drawXAxisTitle(
|
|
295
|
-
svg,
|
|
296
|
-
chart.xlabel,
|
|
297
|
-
m.left + plotW / 2,
|
|
298
|
-
m.top + plotH + 46,
|
|
299
|
-
textColor
|
|
300
|
-
);
|
|
301
|
-
drawYAxisTitle(
|
|
302
|
-
svg,
|
|
303
|
-
chart.ylabel,
|
|
304
|
-
m.top + plotH / 2,
|
|
305
|
-
20,
|
|
306
|
-
leftTint ?? textColor
|
|
307
|
-
);
|
|
308
|
-
if (dual) {
|
|
309
|
-
drawYAxisTitle(
|
|
310
|
-
svg,
|
|
311
|
-
chart.yrlabel,
|
|
312
|
-
m.top + plotH / 2,
|
|
313
|
-
width - 18,
|
|
314
|
-
rightTint ?? textColor
|
|
315
|
-
);
|
|
316
|
-
|
|
317
|
-
// Axis-legend hover targets (§ interactions.ts): a transparent strip over
|
|
318
|
-
// each axis's title + ticks. Appended last so each strip is the topmost
|
|
319
|
-
// (single, flicker-free) mouseenter target while the tinted title/ticks
|
|
320
|
-
// still show through. Only for dual-axis — a sole axis owns every series,
|
|
321
|
-
// so hovering it could not distinguish anything. Sits entirely outside the
|
|
322
|
-
// plot's x-range, so it never shadows point/crosshair hover.
|
|
323
|
-
svg
|
|
324
|
-
.append('rect')
|
|
325
|
-
.attr('class', 'dgmo-axis-legend')
|
|
326
|
-
.attr('data-axis-legend', 'left')
|
|
327
|
-
.attr('x', 0)
|
|
328
|
-
.attr('y', m.top)
|
|
329
|
-
.attr('width', m.left)
|
|
330
|
-
.attr('height', plotH)
|
|
331
|
-
.attr('fill', 'transparent')
|
|
332
|
-
.attr('style', 'cursor:pointer');
|
|
333
|
-
svg
|
|
334
|
-
.append('rect')
|
|
335
|
-
.attr('class', 'dgmo-axis-legend')
|
|
336
|
-
.attr('data-axis-legend', 'right')
|
|
337
|
-
.attr('x', m.left + plotW)
|
|
338
|
-
.attr('y', m.top)
|
|
339
|
-
.attr('width', m.right)
|
|
340
|
-
.attr('height', plotH)
|
|
341
|
-
.attr('fill', 'transparent')
|
|
342
|
-
.attr('style', 'cursor:pointer');
|
|
343
|
-
}
|
|
344
|
-
}
|
package/src/charts-d3/mount.ts
DELETED
|
@@ -1,151 +0,0 @@
|
|
|
1
|
-
// ============================================================
|
|
2
|
-
// Framework-agnostic mount controller for hand-built data charts — SPIKE.
|
|
3
|
-
//
|
|
4
|
-
// The imperative glue a host (React preview, Obsidian, plain DOM) wraps to get
|
|
5
|
-
// a live, interactive D3 data-chart with NO ECharts: render the SVG, inject it,
|
|
6
|
-
// attach the DOM interaction adapter, and expose update()/destroy() for the
|
|
7
|
-
// host's lifecycle. Keeping this here (not in the React component) means it is
|
|
8
|
-
// testable headlessly and reusable across every consumer.
|
|
9
|
-
//
|
|
10
|
-
// A React wrapper is ~15 lines:
|
|
11
|
-
// useEffect(() => {
|
|
12
|
-
// const c = mountD3DataChart(ref.current, content, { theme, palette, onNavigate });
|
|
13
|
-
// return () => c.destroy();
|
|
14
|
-
// }, []);
|
|
15
|
-
// useEffect(() => { ctrl.update(content, { theme, palette }); }, [content, theme, palette]);
|
|
16
|
-
// ============================================================
|
|
17
|
-
|
|
18
|
-
import { render } from '../render';
|
|
19
|
-
import { getPalette } from '../palettes';
|
|
20
|
-
import { renderDataChartD3 } from './index';
|
|
21
|
-
import {
|
|
22
|
-
attachDataChartInteractions,
|
|
23
|
-
type DataChartController,
|
|
24
|
-
} from './interactions';
|
|
25
|
-
|
|
26
|
-
export interface MountD3Opts {
|
|
27
|
-
theme?: 'light' | 'dark' | 'transparent';
|
|
28
|
-
palette?: string;
|
|
29
|
-
/** Forwarded to the interaction adapter: source-line navigation on click. */
|
|
30
|
-
onNavigate?: (line: number) => void;
|
|
31
|
-
mutedColor?: string;
|
|
32
|
-
surface?: string;
|
|
33
|
-
text?: string;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
export interface MountedD3Chart {
|
|
37
|
-
/** Re-render with new content and/or theme/palette; re-attaches interactions. */
|
|
38
|
-
update: (content: string, opts?: Partial<MountD3Opts>) => Promise<void>;
|
|
39
|
-
/** Emphasize the chart element on the given source line (editor cursor sync);
|
|
40
|
-
* pass null to clear. */
|
|
41
|
-
highlight: (line: number | null) => void;
|
|
42
|
-
/** Tear down listeners + overlays and clear the container. */
|
|
43
|
-
destroy: () => void;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
/**
|
|
47
|
-
* Render + mount an interactive D3 data chart into `container`. Returns a
|
|
48
|
-
* controller; call `destroy()` on unmount. No-op interactions if the rendered
|
|
49
|
-
* type carries no interactive markup.
|
|
50
|
-
*/
|
|
51
|
-
function paletteOf(o: MountD3Opts) {
|
|
52
|
-
const theme = o.theme ?? 'light';
|
|
53
|
-
return getPalette(o.palette ?? 'slate')[theme === 'dark' ? 'dark' : 'light'];
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
export function mountD3DataChart(
|
|
57
|
-
container: HTMLElement,
|
|
58
|
-
content: string,
|
|
59
|
-
opts: MountD3Opts = {}
|
|
60
|
-
): MountedD3Chart {
|
|
61
|
-
let interaction: DataChartController | null = null;
|
|
62
|
-
let current: MountD3Opts = { ...opts };
|
|
63
|
-
let text = content;
|
|
64
|
-
let token = 0;
|
|
65
|
-
let pinnedLine: number | null = null;
|
|
66
|
-
let lastW = 0;
|
|
67
|
-
let lastH = 0;
|
|
68
|
-
let resizeTimer: ReturnType<typeof setTimeout> | null = null;
|
|
69
|
-
|
|
70
|
-
const paint = async (): Promise<void> => {
|
|
71
|
-
const mine = ++token;
|
|
72
|
-
// Render at the container's actual size so the chart fills the pane and
|
|
73
|
-
// re-lays-out (not CSS-scales) — matching ECharts' resize behavior. When
|
|
74
|
-
// unmeasured (no layout yet / headless), renderDataChartD3 falls back to
|
|
75
|
-
// its default export dimensions and the ResizeObserver repaints once sized.
|
|
76
|
-
const width = container.clientWidth;
|
|
77
|
-
const height = container.clientHeight;
|
|
78
|
-
lastW = width;
|
|
79
|
-
lastH = height;
|
|
80
|
-
const dims = width > 0 && height > 0 ? { width, height } : undefined;
|
|
81
|
-
|
|
82
|
-
let svg = await renderDataChartD3(
|
|
83
|
-
text,
|
|
84
|
-
current.theme ?? 'light',
|
|
85
|
-
paletteOf(current),
|
|
86
|
-
dims
|
|
87
|
-
);
|
|
88
|
-
// Fallback (parse error / unsupported type) → error card via render().
|
|
89
|
-
if (!svg) {
|
|
90
|
-
const r = await render(text, {
|
|
91
|
-
...(current.theme && { theme: current.theme }),
|
|
92
|
-
...(current.palette && { palette: current.palette }),
|
|
93
|
-
});
|
|
94
|
-
svg = r.svg;
|
|
95
|
-
}
|
|
96
|
-
if (mine !== token) return; // a newer paint superseded this one
|
|
97
|
-
|
|
98
|
-
if (interaction) {
|
|
99
|
-
interaction.destroy();
|
|
100
|
-
interaction = null;
|
|
101
|
-
}
|
|
102
|
-
container.innerHTML = svg;
|
|
103
|
-
const el = container.querySelector('svg');
|
|
104
|
-
if (el) {
|
|
105
|
-
const pal = paletteOf(current);
|
|
106
|
-
interaction = attachDataChartInteractions(el, {
|
|
107
|
-
...(current.onNavigate && { onNavigate: current.onNavigate }),
|
|
108
|
-
mutedColor: current.mutedColor ?? pal.border,
|
|
109
|
-
surface: current.surface ?? pal.surface,
|
|
110
|
-
text: current.text ?? pal.text,
|
|
111
|
-
});
|
|
112
|
-
// re-apply the editor cursor highlight after a re-render
|
|
113
|
-
if (pinnedLine != null) interaction.highlight(pinnedLine);
|
|
114
|
-
}
|
|
115
|
-
};
|
|
116
|
-
|
|
117
|
-
// Re-render (debounced) when the pane size changes.
|
|
118
|
-
const ro =
|
|
119
|
-
typeof ResizeObserver !== 'undefined'
|
|
120
|
-
? new ResizeObserver(() => {
|
|
121
|
-
const w = container.clientWidth;
|
|
122
|
-
const h = container.clientHeight;
|
|
123
|
-
if (w === lastW && h === lastH) return;
|
|
124
|
-
if (resizeTimer) clearTimeout(resizeTimer);
|
|
125
|
-
resizeTimer = setTimeout(() => void paint(), 120);
|
|
126
|
-
})
|
|
127
|
-
: null;
|
|
128
|
-
ro?.observe(container);
|
|
129
|
-
|
|
130
|
-
void paint();
|
|
131
|
-
|
|
132
|
-
return {
|
|
133
|
-
update: async (nextContent: string, patch?: Partial<MountD3Opts>) => {
|
|
134
|
-
text = nextContent;
|
|
135
|
-
current = { ...current, ...patch };
|
|
136
|
-
await paint();
|
|
137
|
-
},
|
|
138
|
-
highlight: (line: number | null) => {
|
|
139
|
-
pinnedLine = line;
|
|
140
|
-
interaction?.highlight(line);
|
|
141
|
-
},
|
|
142
|
-
destroy: () => {
|
|
143
|
-
token++;
|
|
144
|
-
if (resizeTimer) clearTimeout(resizeTimer);
|
|
145
|
-
ro?.disconnect();
|
|
146
|
-
if (interaction) interaction.destroy();
|
|
147
|
-
interaction = null;
|
|
148
|
-
container.innerHTML = '';
|
|
149
|
-
},
|
|
150
|
-
};
|
|
151
|
-
}
|
package/src/charts-d3/pie.ts
DELETED
|
@@ -1,117 +0,0 @@
|
|
|
1
|
-
// ============================================================
|
|
2
|
-
// Hand-built pie / doughnut renderer — SPIKE.
|
|
3
|
-
// ECharts-style: 25% tint fill + full-strength border, external leader-line
|
|
4
|
-
// labels ("Name — value (pct%)"), doughnut center total.
|
|
5
|
-
// ============================================================
|
|
6
|
-
|
|
7
|
-
import { pie as d3pie, arc as d3arc } from 'd3-shape';
|
|
8
|
-
import type { ParsedChart } from '../chart';
|
|
9
|
-
import type { PaletteColors } from '../palettes';
|
|
10
|
-
import { getSegmentColors, shapeFill } from '../palettes/color-utils';
|
|
11
|
-
import { FONT_FAMILY } from '../fonts';
|
|
12
|
-
import { type Svg, fmtNum, tagDatum } from './shared';
|
|
13
|
-
|
|
14
|
-
const LABEL_FONT = 14;
|
|
15
|
-
|
|
16
|
-
export function renderPie(
|
|
17
|
-
svg: Svg,
|
|
18
|
-
chart: ParsedChart,
|
|
19
|
-
width: number,
|
|
20
|
-
height: number,
|
|
21
|
-
palette: PaletteColors,
|
|
22
|
-
isDark: boolean,
|
|
23
|
-
textColor: string,
|
|
24
|
-
topInset: number
|
|
25
|
-
): void {
|
|
26
|
-
const data = chart.data.filter((d) => d.value > 0);
|
|
27
|
-
if (data.length === 0) return;
|
|
28
|
-
// A hole turns the pie into a ring — `pie` + a `hole` directive. (#23)
|
|
29
|
-
const holeRatio = chart.hole;
|
|
30
|
-
const hasHole = holeRatio !== undefined;
|
|
31
|
-
const solid = chart.solidFill === true;
|
|
32
|
-
const total = data.reduce((a, d) => a + d.value, 0);
|
|
33
|
-
|
|
34
|
-
const cx = width / 2;
|
|
35
|
-
const top = topInset + 12;
|
|
36
|
-
const cy = top + (height - top) / 2;
|
|
37
|
-
// Leave horizontal room for the external labels + leader lines.
|
|
38
|
-
const radius = Math.min(width / 2 - 220, (height - top) / 2 - 40);
|
|
39
|
-
|
|
40
|
-
const segColors = getSegmentColors(palette, data.length);
|
|
41
|
-
const strokeFor = (i: number, override?: string) =>
|
|
42
|
-
override ?? segColors[i % segColors.length]!;
|
|
43
|
-
|
|
44
|
-
const arcs = d3pie<{ value: number }>()
|
|
45
|
-
.sort(null)
|
|
46
|
-
.value((d) => d.value)(data.map((d) => ({ value: d.value })));
|
|
47
|
-
|
|
48
|
-
const arcGen = d3arc<(typeof arcs)[number]>()
|
|
49
|
-
.innerRadius(hasHole ? radius * holeRatio! : 0)
|
|
50
|
-
.outerRadius(radius);
|
|
51
|
-
|
|
52
|
-
const g = svg.append('g').attr('transform', `translate(${cx},${cy})`);
|
|
53
|
-
|
|
54
|
-
arcs.forEach((a, i) => {
|
|
55
|
-
const stroke = strokeFor(i, data[i]!.color);
|
|
56
|
-
const fill = solid ? stroke : shapeFill(palette, stroke, isDark);
|
|
57
|
-
const pct = Math.round((data[i]!.value / total) * 100);
|
|
58
|
-
const slice = g
|
|
59
|
-
.append('path')
|
|
60
|
-
.attr('d', arcGen(a) ?? '')
|
|
61
|
-
.attr('fill', fill)
|
|
62
|
-
.attr('stroke', stroke)
|
|
63
|
-
.attr('stroke-width', 1.5);
|
|
64
|
-
tagDatum(slice, {
|
|
65
|
-
line: data[i]!.lineNumber,
|
|
66
|
-
key: data[i]!.label,
|
|
67
|
-
name: data[i]!.label,
|
|
68
|
-
value: `${fmtNum(data[i]!.value)} (${pct}%)`,
|
|
69
|
-
color: stroke,
|
|
70
|
-
});
|
|
71
|
-
|
|
72
|
-
// External leader-line label.
|
|
73
|
-
const mid = (a.startAngle + a.endAngle) / 2 - Math.PI / 2;
|
|
74
|
-
const rightSide = Math.cos(mid) >= 0;
|
|
75
|
-
const x0 = Math.cos(mid) * radius;
|
|
76
|
-
const y0 = Math.sin(mid) * radius;
|
|
77
|
-
const x1 = Math.cos(mid) * (radius + 16);
|
|
78
|
-
const y1 = Math.sin(mid) * (radius + 16);
|
|
79
|
-
const x2 = x1 + (rightSide ? 28 : -28);
|
|
80
|
-
const nm = chart.noName ? '' : data[i]!.label;
|
|
81
|
-
const tail = [
|
|
82
|
-
chart.noValue ? '' : fmtNum(data[i]!.value),
|
|
83
|
-
chart.noPercent ? '' : `(${pct}%)`,
|
|
84
|
-
]
|
|
85
|
-
.filter(Boolean)
|
|
86
|
-
.join(' ');
|
|
87
|
-
const label = [nm, tail].filter(Boolean).join(' — ');
|
|
88
|
-
if (label) {
|
|
89
|
-
g.append('polyline')
|
|
90
|
-
.attr('points', `${x0},${y0} ${x1},${y1} ${x2},${y1}`)
|
|
91
|
-
.attr('fill', 'none')
|
|
92
|
-
.attr('stroke', stroke)
|
|
93
|
-
.attr('stroke-width', 1);
|
|
94
|
-
g.append('text')
|
|
95
|
-
.attr('x', x2 + (rightSide ? 4 : -4))
|
|
96
|
-
.attr('y', y1 + 4)
|
|
97
|
-
.attr('text-anchor', rightSide ? 'start' : 'end')
|
|
98
|
-
.attr('fill', textColor)
|
|
99
|
-
.attr('font-size', LABEL_FONT)
|
|
100
|
-
.attr('font-family', FONT_FAMILY)
|
|
101
|
-
.text(label);
|
|
102
|
-
}
|
|
103
|
-
});
|
|
104
|
-
|
|
105
|
-
// Center total — shown by default whenever there is a hole, unless suppressed
|
|
106
|
-
// with `no-center-total`. (#23)
|
|
107
|
-
if (hasHole && !chart.noCenterTotal) {
|
|
108
|
-
g.append('text')
|
|
109
|
-
.attr('text-anchor', 'middle')
|
|
110
|
-
.attr('y', 8)
|
|
111
|
-
.attr('fill', textColor)
|
|
112
|
-
.attr('font-size', 26)
|
|
113
|
-
.attr('font-weight', 700)
|
|
114
|
-
.attr('font-family', FONT_FAMILY)
|
|
115
|
-
.text(fmtNum(total));
|
|
116
|
-
}
|
|
117
|
-
}
|
package/src/charts-d3/polar.ts
DELETED
|
@@ -1,89 +0,0 @@
|
|
|
1
|
-
// ============================================================
|
|
2
|
-
// Hand-built polar-area (rose) renderer — SPIKE (Tier 2).
|
|
3
|
-
// Equal-angle sectors; radius proportional to value (ECharts roseType:radius).
|
|
4
|
-
// ============================================================
|
|
5
|
-
|
|
6
|
-
import { arc as d3arc } from 'd3-shape';
|
|
7
|
-
import type { ParsedChart } from '../chart';
|
|
8
|
-
import type { PaletteColors } from '../palettes';
|
|
9
|
-
import { FONT_FAMILY } from '../fonts';
|
|
10
|
-
import { getSegmentColors, shapeFill } from '../palettes/color-utils';
|
|
11
|
-
import { type Svg, fmtNum, tagDatum } from './shared';
|
|
12
|
-
|
|
13
|
-
export function renderPolarArea(
|
|
14
|
-
svg: Svg,
|
|
15
|
-
chart: ParsedChart,
|
|
16
|
-
width: number,
|
|
17
|
-
height: number,
|
|
18
|
-
palette: PaletteColors,
|
|
19
|
-
isDark: boolean,
|
|
20
|
-
textColor: string,
|
|
21
|
-
topInset: number
|
|
22
|
-
): void {
|
|
23
|
-
const data = chart.data.filter((d) => d.value > 0);
|
|
24
|
-
const n = data.length;
|
|
25
|
-
if (n === 0) return;
|
|
26
|
-
const solid = chart.solidFill === true;
|
|
27
|
-
const total = data.reduce((a, d) => a + d.value, 0);
|
|
28
|
-
const maxValue = Math.max(...data.map((d) => d.value)) || 1;
|
|
29
|
-
|
|
30
|
-
const cx = width / 2;
|
|
31
|
-
const top = topInset + 8;
|
|
32
|
-
const cy = top + (height - top) / 2;
|
|
33
|
-
const outerR = Math.min(width / 2 - 200, (height - top) / 2 - 40);
|
|
34
|
-
const innerR = outerR * 0.14;
|
|
35
|
-
|
|
36
|
-
const segColors = getSegmentColors(palette, n);
|
|
37
|
-
const g = svg.append('g').attr('transform', `translate(${cx},${cy})`);
|
|
38
|
-
const seg = (2 * Math.PI) / n;
|
|
39
|
-
|
|
40
|
-
data.forEach((d, i) => {
|
|
41
|
-
const stroke = d.color ?? segColors[i % segColors.length]!;
|
|
42
|
-
const r = innerR + (d.value / maxValue) * (outerR - innerR);
|
|
43
|
-
const a0 = i * seg;
|
|
44
|
-
const a1 = a0 + seg;
|
|
45
|
-
const arcGen = d3arc<unknown>()
|
|
46
|
-
.innerRadius(innerR)
|
|
47
|
-
.outerRadius(r)
|
|
48
|
-
.startAngle(a0)
|
|
49
|
-
.endAngle(a1);
|
|
50
|
-
const pct = Math.round((d.value / total) * 100);
|
|
51
|
-
const sector = g
|
|
52
|
-
.append('path')
|
|
53
|
-
.attr('d', arcGen({}) ?? '')
|
|
54
|
-
.attr('fill', solid ? stroke : shapeFill(palette, stroke, isDark))
|
|
55
|
-
.attr('stroke', stroke)
|
|
56
|
-
.attr('stroke-width', 2);
|
|
57
|
-
tagDatum(sector, {
|
|
58
|
-
line: d.lineNumber,
|
|
59
|
-
key: d.label,
|
|
60
|
-
name: d.label,
|
|
61
|
-
value: `${fmtNum(d.value)} (${pct}%)`,
|
|
62
|
-
color: stroke,
|
|
63
|
-
});
|
|
64
|
-
|
|
65
|
-
// external label at sector mid-angle (d3 angle 0 = top, clockwise)
|
|
66
|
-
const mid = a0 + seg / 2 - Math.PI / 2;
|
|
67
|
-
const rightSide = Math.cos(mid) >= 0;
|
|
68
|
-
const lx = Math.cos(mid) * (outerR + 14);
|
|
69
|
-
const ly = Math.sin(mid) * (outerR + 14);
|
|
70
|
-
const nm = chart.noName ? '' : d.label;
|
|
71
|
-
const tail = [
|
|
72
|
-
chart.noValue ? '' : fmtNum(d.value),
|
|
73
|
-
chart.noPercent ? '' : `(${pct}%)`,
|
|
74
|
-
]
|
|
75
|
-
.filter(Boolean)
|
|
76
|
-
.join(' ');
|
|
77
|
-
const label = [nm, tail].filter(Boolean).join(' — ');
|
|
78
|
-
if (label) {
|
|
79
|
-
g.append('text')
|
|
80
|
-
.attr('x', lx + (rightSide ? 4 : -4))
|
|
81
|
-
.attr('y', ly + 4)
|
|
82
|
-
.attr('text-anchor', rightSide ? 'start' : 'end')
|
|
83
|
-
.attr('fill', textColor)
|
|
84
|
-
.attr('font-size', 13)
|
|
85
|
-
.attr('font-family', FONT_FAMILY)
|
|
86
|
-
.text(label);
|
|
87
|
-
}
|
|
88
|
-
});
|
|
89
|
-
}
|
package/src/charts-d3/radar.ts
DELETED
|
Binary file
|
package/src/charts-d3/sankey.ts
DELETED
|
Binary file
|