@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
|
@@ -1,438 +0,0 @@
|
|
|
1
|
-
// ============================================================
|
|
2
|
-
// Treemap — Radial (sunburst) D3 SVG Renderer
|
|
3
|
-
// ============================================================
|
|
4
|
-
//
|
|
5
|
-
// Emits a static, full-tree sunburst: concentric rings (one per depth level)
|
|
6
|
-
// around a solid center disc that holds the CHART TITLE + grand total (the
|
|
7
|
-
// synthetic root — NOT a node label; works uniformly for single- and multi-root
|
|
8
|
-
// sources). Color reuses the treemap color engine (`treemap-shared.ts`): branch
|
|
9
|
-
// base hue at the inner ring, tinted outward by depth (floored so the outer ring
|
|
10
|
-
// stays legible), or tag / heat modes.
|
|
11
|
-
//
|
|
12
|
-
// Static export only — no interactive chrome (no drill breadcrumb / tooltip /
|
|
13
|
-
// mode switcher). References `charts-d3/pie.ts` for the arc() + mid-angle label
|
|
14
|
-
// math, but does not import it (different data model: hierarchy vs flat chart).
|
|
15
|
-
|
|
16
|
-
import * as d3Selection from 'd3-selection';
|
|
17
|
-
import { arc as d3arc } from 'd3-shape';
|
|
18
|
-
import { FONT_FAMILY } from '../fonts';
|
|
19
|
-
import { contrastText, getSeriesColors } from '../palettes/color-utils';
|
|
20
|
-
import { tagAttrKey } from '../utils/tag-groups';
|
|
21
|
-
import { measureText } from '../utils/text-measure';
|
|
22
|
-
import { renderIntegratedLegend } from '../utils/legend-integration';
|
|
23
|
-
import { getMaxLegendReservedHeight } from '../utils/legend-layout';
|
|
24
|
-
import { LEGEND_GROUP_GAP } from '../utils/legend-constants';
|
|
25
|
-
import type { LegendPosition } from '../utils/legend-types';
|
|
26
|
-
import type { PaletteColors } from '../palettes';
|
|
27
|
-
import type { D3ExportDimensions } from '../utils/d3-types';
|
|
28
|
-
import type { ParsedTreemap, TreemapColorMode } from './types';
|
|
29
|
-
import { layoutTreemapRadial, type RadialCell } from './layout-radial';
|
|
30
|
-
import {
|
|
31
|
-
buildHeatScale,
|
|
32
|
-
buildLegend,
|
|
33
|
-
compactNumber,
|
|
34
|
-
formatPct,
|
|
35
|
-
resolveCellColor,
|
|
36
|
-
resolveColorMode,
|
|
37
|
-
type CellColorContext,
|
|
38
|
-
} from './treemap-shared';
|
|
39
|
-
|
|
40
|
-
const PADDING = 12;
|
|
41
|
-
|
|
42
|
-
/** Standard placement every dgmo legend uses: centered, under the title. */
|
|
43
|
-
const LEGEND_POSITION: LegendPosition = {
|
|
44
|
-
placement: 'top-center',
|
|
45
|
-
titleRelation: 'below-title',
|
|
46
|
-
};
|
|
47
|
-
|
|
48
|
-
/** Small-arc dropout threshold (ADR-5/AC7): drop inline labels below 6°. */
|
|
49
|
-
const MIN_LABEL_SWEEP = (6 * Math.PI) / 180; // ≈ 0.105 rad
|
|
50
|
-
|
|
51
|
-
/** Floor for shrink-to-fit label text — below this it's ellipsized instead. */
|
|
52
|
-
const MIN_LABEL_FS = 8;
|
|
53
|
-
|
|
54
|
-
export interface TreemapRadialRenderOptions {
|
|
55
|
-
/** Color mode override (app's runtime switcher). Defaults to source. */
|
|
56
|
-
colorMode?: TreemapColorMode;
|
|
57
|
-
/** Interactive render budget. Export omits it → full tree. */
|
|
58
|
-
maxDepth?: number;
|
|
59
|
-
/** Shift the branch-hue index (drilled-into view keeps its expanded hue). */
|
|
60
|
-
colorOffset?: number;
|
|
61
|
-
exportMode?: boolean;
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
/** Render for CLI/export (full tree, no interactive chrome). */
|
|
65
|
-
export function renderTreemapRadialForExport(
|
|
66
|
-
container: HTMLDivElement,
|
|
67
|
-
parsed: ParsedTreemap,
|
|
68
|
-
palette: PaletteColors,
|
|
69
|
-
isDark: boolean,
|
|
70
|
-
exportDims?: D3ExportDimensions,
|
|
71
|
-
options?: TreemapRadialRenderOptions
|
|
72
|
-
): void {
|
|
73
|
-
renderTreemapRadial(container, parsed, palette, isDark, exportDims, {
|
|
74
|
-
exportMode: true,
|
|
75
|
-
...options,
|
|
76
|
-
});
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
export function renderTreemapRadial(
|
|
80
|
-
container: HTMLDivElement,
|
|
81
|
-
parsed: ParsedTreemap,
|
|
82
|
-
palette: PaletteColors,
|
|
83
|
-
isDark: boolean,
|
|
84
|
-
exportDims?: D3ExportDimensions,
|
|
85
|
-
options: TreemapRadialRenderOptions = {}
|
|
86
|
-
): void {
|
|
87
|
-
if (parsed.error || parsed.roots.length === 0) return;
|
|
88
|
-
void isDark;
|
|
89
|
-
|
|
90
|
-
d3Selection.select(container).selectAll(':not([data-d3-tooltip])').remove();
|
|
91
|
-
const width = exportDims?.width ?? container.clientWidth;
|
|
92
|
-
const height = exportDims?.height ?? container.clientHeight;
|
|
93
|
-
if (width <= 0 || height <= 0) return;
|
|
94
|
-
|
|
95
|
-
const mode = resolveColorMode(parsed, options.colorMode);
|
|
96
|
-
const opts = parsed.options;
|
|
97
|
-
const exportMode = options.exportMode ?? false;
|
|
98
|
-
const seriesColors = getSeriesColors(palette);
|
|
99
|
-
|
|
100
|
-
const heat = buildHeatScale(parsed, palette);
|
|
101
|
-
const legend = !opts.noLegend
|
|
102
|
-
? buildLegend(mode, parsed, heat, seriesColors, options.colorOffset ?? 0)
|
|
103
|
-
: null;
|
|
104
|
-
|
|
105
|
-
const legendReserve = legend
|
|
106
|
-
? getMaxLegendReservedHeight(
|
|
107
|
-
{ groups: legend.groups, position: LEGEND_POSITION, mode: 'preview' },
|
|
108
|
-
width
|
|
109
|
-
) + LEGEND_GROUP_GAP
|
|
110
|
-
: 0;
|
|
111
|
-
|
|
112
|
-
// Plot area below the (optional) legend band. The chart title lives in the
|
|
113
|
-
// center disc for radial mode, so no top title band is reserved.
|
|
114
|
-
const areaY = legendReserve + PADDING;
|
|
115
|
-
const areaW = Math.max(1, width - PADDING * 2);
|
|
116
|
-
const areaH = Math.max(1, height - areaY - PADDING);
|
|
117
|
-
const radius = Math.max(1, Math.min(areaW, areaH) / 2 - PADDING);
|
|
118
|
-
const cx = width / 2;
|
|
119
|
-
const cy = areaY + areaH / 2;
|
|
120
|
-
|
|
121
|
-
const layout = layoutTreemapRadial(parsed.roots, {
|
|
122
|
-
radius,
|
|
123
|
-
...(options.maxDepth !== undefined && { maxDepth: options.maxDepth }),
|
|
124
|
-
});
|
|
125
|
-
|
|
126
|
-
// ── SVG root ───────────────────────────────────────────────
|
|
127
|
-
const svg = d3Selection
|
|
128
|
-
.select(container)
|
|
129
|
-
.append('svg')
|
|
130
|
-
.attr('width', width)
|
|
131
|
-
.attr('height', height)
|
|
132
|
-
.attr('viewBox', `0 0 ${width} ${height}`)
|
|
133
|
-
.attr('preserveAspectRatio', 'xMidYMin meet')
|
|
134
|
-
.attr('xmlns', 'http://www.w3.org/2000/svg')
|
|
135
|
-
.attr('class', 'dgmo-treemap dgmo-treemap-radial')
|
|
136
|
-
.style('font-family', FONT_FAMILY);
|
|
137
|
-
|
|
138
|
-
// Curved arc labels reference per-arc paths kept in <defs> (via <textPath>).
|
|
139
|
-
const defs = svg.append('defs');
|
|
140
|
-
|
|
141
|
-
svg
|
|
142
|
-
.append('rect')
|
|
143
|
-
.attr('width', width)
|
|
144
|
-
.attr('height', height)
|
|
145
|
-
.attr('fill', palette.bg);
|
|
146
|
-
|
|
147
|
-
const plot = svg
|
|
148
|
-
.append('g')
|
|
149
|
-
.attr('class', 'dgmo-treemap-cells')
|
|
150
|
-
.attr('transform', `translate(${cx},${cy})`);
|
|
151
|
-
|
|
152
|
-
const activeGroup =
|
|
153
|
-
parsed.tagGroups.length > 0 ? parsed.tagGroups[0]!.name : null;
|
|
154
|
-
const rootIndexByLabel = new Map(parsed.roots.map((r, i) => [r.label, i]));
|
|
155
|
-
const colorCtx: CellColorContext = {
|
|
156
|
-
mode,
|
|
157
|
-
heat,
|
|
158
|
-
tagGroups: parsed.tagGroups,
|
|
159
|
-
activeGroup,
|
|
160
|
-
rootIndexByLabel,
|
|
161
|
-
seriesColors,
|
|
162
|
-
colorOffset: options.colorOffset ?? 0,
|
|
163
|
-
bg: palette.bg,
|
|
164
|
-
};
|
|
165
|
-
|
|
166
|
-
const arcGen = d3arc<RadialCell>()
|
|
167
|
-
.startAngle((c) => c.startAngle)
|
|
168
|
-
.endAngle((c) => c.endAngle)
|
|
169
|
-
.innerRadius((c) => c.innerR)
|
|
170
|
-
.outerRadius((c) => c.outerR)
|
|
171
|
-
.padAngle(0.004)
|
|
172
|
-
.padRadius(radius);
|
|
173
|
-
|
|
174
|
-
// ── Arcs ───────────────────────────────────────────────────
|
|
175
|
-
let labelSeq = 0;
|
|
176
|
-
if (!layout.isEmpty) {
|
|
177
|
-
for (const cell of layout.cells) {
|
|
178
|
-
const fill = resolveCellColor(cell, colorCtx);
|
|
179
|
-
const g = plot
|
|
180
|
-
.append('g')
|
|
181
|
-
.attr('class', 'dgmo-treemap-cell')
|
|
182
|
-
.attr('data-node-path', cell.path.join(' / '));
|
|
183
|
-
if (cell.lineNumber !== undefined) {
|
|
184
|
-
g.attr('data-line-number', cell.lineNumber);
|
|
185
|
-
}
|
|
186
|
-
if (cell.heat !== undefined) g.attr('data-heat', cell.heat);
|
|
187
|
-
if (cell.node && activeGroup) {
|
|
188
|
-
const key = tagAttrKey(activeGroup);
|
|
189
|
-
const tagVal = cell.node.metadata[key];
|
|
190
|
-
if (tagVal) g.attr(`data-tag-${key}`, tagVal);
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
g.append('path')
|
|
194
|
-
.attr(
|
|
195
|
-
'class',
|
|
196
|
-
cell.isContainer ? 'dgmo-treemap-arc branch' : 'dgmo-treemap-arc'
|
|
197
|
-
)
|
|
198
|
-
.attr('d', arcGen(cell) ?? '')
|
|
199
|
-
.attr('fill', fill)
|
|
200
|
-
.attr('stroke', palette.bg)
|
|
201
|
-
.attr('stroke-width', 1);
|
|
202
|
-
|
|
203
|
-
drawArcLabel(
|
|
204
|
-
g,
|
|
205
|
-
defs,
|
|
206
|
-
`dgmo-arc-lbl-${labelSeq++}`,
|
|
207
|
-
cell,
|
|
208
|
-
opts,
|
|
209
|
-
palette,
|
|
210
|
-
fill
|
|
211
|
-
);
|
|
212
|
-
}
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
// ── Center disc: chart title + grand total (or empty-state) ─
|
|
216
|
-
drawCenterDisc(
|
|
217
|
-
plot,
|
|
218
|
-
parsed,
|
|
219
|
-
layout.discRadius,
|
|
220
|
-
layout.total,
|
|
221
|
-
layout.isEmpty,
|
|
222
|
-
palette
|
|
223
|
-
);
|
|
224
|
-
|
|
225
|
-
// ── Legend ─────────────────────────────────────────────────
|
|
226
|
-
if (legend) {
|
|
227
|
-
const legendG = svg
|
|
228
|
-
.append('g')
|
|
229
|
-
.attr('class', 'dgmo-treemap-legend')
|
|
230
|
-
.attr('transform', 'translate(0, 0)');
|
|
231
|
-
renderIntegratedLegend(legendG, {
|
|
232
|
-
groups: legend.groups,
|
|
233
|
-
palette: {
|
|
234
|
-
bg: palette.bg,
|
|
235
|
-
surface: palette.surface,
|
|
236
|
-
text: palette.text,
|
|
237
|
-
textMuted: palette.textMuted,
|
|
238
|
-
primary: palette.primary,
|
|
239
|
-
},
|
|
240
|
-
isDark,
|
|
241
|
-
width,
|
|
242
|
-
mode: exportMode ? 'export' : 'preview',
|
|
243
|
-
position: LEGEND_POSITION,
|
|
244
|
-
activeGroup: legend.activeGroup,
|
|
245
|
-
showInactivePills: !exportMode,
|
|
246
|
-
showEmptyGroups: !exportMode,
|
|
247
|
-
});
|
|
248
|
-
}
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
// ============================================================
|
|
252
|
-
// Center disc
|
|
253
|
-
// ============================================================
|
|
254
|
-
|
|
255
|
-
function drawCenterDisc(
|
|
256
|
-
plot: d3Selection.Selection<SVGGElement, unknown, null, undefined>,
|
|
257
|
-
parsed: ParsedTreemap,
|
|
258
|
-
discRadius: number,
|
|
259
|
-
total: number,
|
|
260
|
-
isEmpty: boolean,
|
|
261
|
-
palette: PaletteColors
|
|
262
|
-
): void {
|
|
263
|
-
const disc = plot.append('g').attr('class', 'dgmo-treemap-disc');
|
|
264
|
-
disc
|
|
265
|
-
.append('circle')
|
|
266
|
-
.attr('r', discRadius)
|
|
267
|
-
.attr('fill', palette.surface)
|
|
268
|
-
.attr('stroke', palette.bg)
|
|
269
|
-
.attr('stroke-width', 1);
|
|
270
|
-
|
|
271
|
-
const ink = contrastText(
|
|
272
|
-
palette.surface,
|
|
273
|
-
palette.textOnFillLight,
|
|
274
|
-
palette.textOnFillDark
|
|
275
|
-
);
|
|
276
|
-
const maxW = discRadius * 1.7;
|
|
277
|
-
|
|
278
|
-
if (isEmpty) {
|
|
279
|
-
disc
|
|
280
|
-
.append('text')
|
|
281
|
-
.attr('class', 'dgmo-treemap-empty')
|
|
282
|
-
.attr('text-anchor', 'middle')
|
|
283
|
-
.attr('y', 4)
|
|
284
|
-
.attr('fill', ink)
|
|
285
|
-
.attr('font-size', clampFs(13, discRadius))
|
|
286
|
-
.text(clip('No values to chart', maxW, 13));
|
|
287
|
-
return;
|
|
288
|
-
}
|
|
289
|
-
|
|
290
|
-
const title = parsed.title ?? '';
|
|
291
|
-
const totalStr = compactNumber(total);
|
|
292
|
-
const titleFs = clampFs(15, discRadius);
|
|
293
|
-
const totalFs = clampFs(22, discRadius);
|
|
294
|
-
|
|
295
|
-
if (title) {
|
|
296
|
-
const t = disc
|
|
297
|
-
.append('text')
|
|
298
|
-
.attr('class', 'dgmo-treemap-disc-title')
|
|
299
|
-
.attr('text-anchor', 'middle')
|
|
300
|
-
.attr('y', -4)
|
|
301
|
-
.attr('fill', ink)
|
|
302
|
-
.attr('font-size', titleFs)
|
|
303
|
-
.attr('font-weight', 600)
|
|
304
|
-
.text(clip(title, maxW, titleFs));
|
|
305
|
-
if (parsed.titleLineNumber !== null) {
|
|
306
|
-
t.attr('data-line-number', parsed.titleLineNumber);
|
|
307
|
-
}
|
|
308
|
-
}
|
|
309
|
-
|
|
310
|
-
disc
|
|
311
|
-
.append('text')
|
|
312
|
-
.attr('class', 'dgmo-treemap-disc-total')
|
|
313
|
-
.attr('text-anchor', 'middle')
|
|
314
|
-
.attr('y', title ? titleFs : 8)
|
|
315
|
-
.attr('fill', ink)
|
|
316
|
-
.attr('font-size', totalFs)
|
|
317
|
-
.attr('font-weight', 700)
|
|
318
|
-
.text(clip(totalStr, maxW, totalFs));
|
|
319
|
-
}
|
|
320
|
-
|
|
321
|
-
// ============================================================
|
|
322
|
-
// Arc labels — curved along the ring (<textPath>) + small-arc dropout
|
|
323
|
-
// ============================================================
|
|
324
|
-
|
|
325
|
-
function drawArcLabel(
|
|
326
|
-
g: d3Selection.Selection<SVGGElement, unknown, null, undefined>,
|
|
327
|
-
defs: d3Selection.Selection<SVGDefsElement, unknown, null, undefined>,
|
|
328
|
-
pathId: string,
|
|
329
|
-
cell: RadialCell,
|
|
330
|
-
opts: ParsedTreemap['options'],
|
|
331
|
-
palette: PaletteColors,
|
|
332
|
-
fill: string
|
|
333
|
-
): void {
|
|
334
|
-
const sweep = cell.endAngle - cell.startAngle;
|
|
335
|
-
// AC7 small-arc dropout: no inline label below the threshold.
|
|
336
|
-
if (sweep < MIN_LABEL_SWEEP) return;
|
|
337
|
-
|
|
338
|
-
const ringThickness = cell.outerR - cell.innerR;
|
|
339
|
-
if (ringThickness < 10) return;
|
|
340
|
-
|
|
341
|
-
const ink = contrastText(
|
|
342
|
-
fill,
|
|
343
|
-
palette.textOnFillLight,
|
|
344
|
-
palette.textOnFillDark
|
|
345
|
-
);
|
|
346
|
-
|
|
347
|
-
const rMid = (cell.innerR + cell.outerR) / 2;
|
|
348
|
-
const maxFs = clampFs(13, ringThickness);
|
|
349
|
-
|
|
350
|
-
const valParts = [
|
|
351
|
-
opts.noValues ? '' : compactNumber(cell.value),
|
|
352
|
-
opts.noPercent ? '' : formatPct(cell.pctOfRoot),
|
|
353
|
-
].filter(Boolean);
|
|
354
|
-
const valStr = valParts.join(' · ');
|
|
355
|
-
const full = valStr ? `${cell.label} ${valStr}` : cell.label;
|
|
356
|
-
|
|
357
|
-
// Arc length available at the label's radius (small margin off the dividers).
|
|
358
|
-
const availLen = sweep * rMid * 0.94;
|
|
359
|
-
|
|
360
|
-
// Shrink-to-fit: keep the full label by dropping the font size (proportional
|
|
361
|
-
// to the overflow), floored at MIN_LABEL_FS. Only ellipsize if it still won't
|
|
362
|
-
// fit at the floor. `measureText` width scales ~linearly with font size.
|
|
363
|
-
let fs = maxFs;
|
|
364
|
-
const wAtMax = measureText(full, maxFs);
|
|
365
|
-
if (wAtMax > availLen) {
|
|
366
|
-
fs = Math.max(MIN_LABEL_FS, Math.floor((maxFs * availLen) / wAtMax));
|
|
367
|
-
}
|
|
368
|
-
const text = clip(full, availLen, fs);
|
|
369
|
-
if (!text) return;
|
|
370
|
-
|
|
371
|
-
drawCurvedLabel(g, defs, pathId, cell, rMid, fs, text, ink);
|
|
372
|
-
}
|
|
373
|
-
|
|
374
|
-
/** Curved label: text flows along the ring on a per-arc <textPath>. On the
|
|
375
|
-
* bottom half the path is reversed so the text is never upside-down. */
|
|
376
|
-
function drawCurvedLabel(
|
|
377
|
-
g: d3Selection.Selection<SVGGElement, unknown, null, undefined>,
|
|
378
|
-
defs: d3Selection.Selection<SVGDefsElement, unknown, null, undefined>,
|
|
379
|
-
pathId: string,
|
|
380
|
-
cell: RadialCell,
|
|
381
|
-
rMid: number,
|
|
382
|
-
fs: number,
|
|
383
|
-
text: string,
|
|
384
|
-
ink: string
|
|
385
|
-
): void {
|
|
386
|
-
const mid = (cell.startAngle + cell.endAngle) / 2;
|
|
387
|
-
const bottom = mid > Math.PI / 2 && mid < (3 * Math.PI) / 2;
|
|
388
|
-
|
|
389
|
-
// Baseline sits ON the path; nudge the path radius so the text's optical
|
|
390
|
-
// centre lands on rMid (glyphs grow outward on top, inward on the reversed
|
|
391
|
-
// bottom path — opposite radial offsets keep both visually centred).
|
|
392
|
-
const rPath = bottom ? rMid + fs * 0.32 : rMid - fs * 0.32;
|
|
393
|
-
|
|
394
|
-
// SVG polar: 0 rad = 12 o'clock, clockwise. Reverse endpoints + sweep-flag on
|
|
395
|
-
// the bottom half to flip the baseline upright.
|
|
396
|
-
const [a0, a1] = bottom
|
|
397
|
-
? [cell.endAngle, cell.startAngle]
|
|
398
|
-
: [cell.startAngle, cell.endAngle];
|
|
399
|
-
const p0x = Math.sin(a0) * rPath;
|
|
400
|
-
const p0y = -Math.cos(a0) * rPath;
|
|
401
|
-
const p1x = Math.sin(a1) * rPath;
|
|
402
|
-
const p1y = -Math.cos(a1) * rPath;
|
|
403
|
-
const sweepFlag = bottom ? 0 : 1;
|
|
404
|
-
const d = `M ${p0x.toFixed(2)} ${p0y.toFixed(2)} A ${rPath.toFixed(2)} ${rPath.toFixed(2)} 0 0 ${sweepFlag} ${p1x.toFixed(2)} ${p1y.toFixed(2)}`;
|
|
405
|
-
|
|
406
|
-
defs.append('path').attr('id', pathId).attr('d', d);
|
|
407
|
-
|
|
408
|
-
g.append('text')
|
|
409
|
-
.attr('class', 'dgmo-treemap-label')
|
|
410
|
-
.attr('font-size', fs)
|
|
411
|
-
.attr('font-weight', 600)
|
|
412
|
-
.attr('fill', ink)
|
|
413
|
-
.append('textPath')
|
|
414
|
-
.attr('href', `#${pathId}`)
|
|
415
|
-
.attr('startOffset', '50%')
|
|
416
|
-
.attr('text-anchor', 'middle')
|
|
417
|
-
.text(text);
|
|
418
|
-
}
|
|
419
|
-
|
|
420
|
-
// ============================================================
|
|
421
|
-
// Small helpers
|
|
422
|
-
// ============================================================
|
|
423
|
-
|
|
424
|
-
function clampFs(base: number, thickness: number): number {
|
|
425
|
-
return Math.max(8, Math.min(base, Math.round(thickness * 0.9)));
|
|
426
|
-
}
|
|
427
|
-
|
|
428
|
-
function clip(s: string, maxWidth: number, fs: number): string {
|
|
429
|
-
if (measureText(s, fs) <= maxWidth) return s;
|
|
430
|
-
let lo = 0;
|
|
431
|
-
let hi = s.length;
|
|
432
|
-
while (lo < hi) {
|
|
433
|
-
const mid = Math.ceil((lo + hi) / 2);
|
|
434
|
-
if (measureText(s.slice(0, mid) + '…', fs) <= maxWidth) lo = mid;
|
|
435
|
-
else hi = mid - 1;
|
|
436
|
-
}
|
|
437
|
-
return lo <= 0 ? '' : s.slice(0, lo) + '…';
|
|
438
|
-
}
|