@diagrammo/dgmo 0.23.0 → 0.24.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.cjs CHANGED
@@ -26492,7 +26492,7 @@ function renderBoxesAndLines(container, parsed, layout, palette, isDark, options
26492
26492
  const VALUE_NAME = hasRamp ? parsed.boxMetric?.trim() || "Value" : null;
26493
26493
  const matchColorGroup = (v) => {
26494
26494
  const lv = v.trim().toLowerCase();
26495
- if (lv === "none") return null;
26495
+ if (lv === "" || lv === "none") return null;
26496
26496
  const tg = parsed.tagGroups.find((g) => g.name.toLowerCase() === lv);
26497
26497
  if (tg) return tg.name;
26498
26498
  if (lv === VALUE_NAME?.toLowerCase()) return VALUE_NAME;
@@ -26833,6 +26833,22 @@ function renderBoxesAndLines(container, parsed, layout, palette, isDark, options
26833
26833
  const tooltipText = fullText.length > 200 ? fullText.slice(0, 199) + "\u2026" : fullText;
26834
26834
  nodeG.append("title").text(tooltipText);
26835
26835
  }
26836
+ } else if (parsed.showValues && node.value !== void 0) {
26837
+ const valueLabel = parsed.boxMetric ? `${parsed.boxMetric}: ${node.value}` : String(node.value);
26838
+ const headerH = ln.height / 2;
26839
+ const sepY = -ln.height / 2 + headerH;
26840
+ const fitted = fitLabelToHeader(node.label, ln.width, 2);
26841
+ const labelLineH = fitted.fontSize * 1.3;
26842
+ const labelTotalH = fitted.lines.length * labelLineH;
26843
+ const headerCenterY = -ln.height / 2 + headerH / 2;
26844
+ for (let li = 0; li < fitted.lines.length; li++) {
26845
+ nodeG.append("text").attr("x", 0).attr(
26846
+ "y",
26847
+ headerCenterY - labelTotalH / 2 + labelLineH / 2 + li * labelLineH
26848
+ ).attr("text-anchor", "middle").attr("dominant-baseline", "central").attr("font-size", fitted.fontSize).attr("font-weight", "600").attr("fill", colors.text).text(fitted.lines[li]);
26849
+ }
26850
+ nodeG.append("line").attr("x1", -ln.width / 2).attr("y1", sepY).attr("x2", ln.width / 2).attr("y2", sepY).attr("stroke", colors.stroke).attr("stroke-opacity", 0.3).attr("stroke-width", 1);
26851
+ nodeG.append("text").attr("class", "bl-node-value").attr("x", 0).attr("y", (sepY + ln.height / 2) / 2).attr("text-anchor", "middle").attr("dominant-baseline", "central").attr("font-size", VALUE_FONT_SIZE).attr("fill", colors.text).attr("opacity", 0.85).text(valueLabel);
26836
26852
  } else {
26837
26853
  const maxLabelLines = Math.max(
26838
26854
  2,
@@ -26845,21 +26861,16 @@ function renderBoxesAndLines(container, parsed, layout, palette, isDark, options
26845
26861
  nodeG.append("text").attr("x", 0).attr("y", -totalH / 2 + lineH / 2 + li * lineH).attr("text-anchor", "middle").attr("dominant-baseline", "central").attr("font-size", fitted.fontSize).attr("font-weight", "600").attr("fill", colors.text).text(fitted.lines[li]);
26846
26862
  }
26847
26863
  }
26848
- if (parsed.showValues && node.value !== void 0) {
26864
+ if (parsed.showValues && node.value !== void 0 && desc && desc.length > 0 && !hideDescriptions) {
26849
26865
  const valueText = String(node.value);
26850
- const descShown = !!(desc && desc.length > 0 && !hideDescriptions);
26851
- if (descShown) {
26852
- const padX = 6;
26853
- const padY = 5;
26854
- const bw = valueText.length * VALUE_FONT_SIZE * CHAR_WIDTH_RATIO2 + 8;
26855
- const bh = VALUE_FONT_SIZE + 4;
26856
- const bx = ln.width / 2 - bw - 4;
26857
- const by = -ln.height / 2 + 4;
26858
- nodeG.append("rect").attr("x", bx).attr("y", by).attr("width", bw).attr("height", bh).attr("rx", 3).attr("fill", palette.bg).attr("opacity", 0.85);
26859
- nodeG.append("text").attr("class", "bl-node-value").attr("x", bx + bw - padX).attr("y", by + padY).attr("text-anchor", "end").attr("dominant-baseline", "central").attr("font-size", VALUE_FONT_SIZE).attr("font-weight", "600").attr("fill", palette.textMuted).text(valueText);
26860
- } else {
26861
- nodeG.append("text").attr("class", "bl-node-value").attr("x", 0).attr("y", ln.height / 2 - VALUE_FONT_SIZE).attr("text-anchor", "middle").attr("dominant-baseline", "central").attr("font-size", VALUE_FONT_SIZE).attr("font-weight", "600").attr("fill", colors.text).attr("opacity", 0.8).text(valueText);
26862
- }
26866
+ const padX = 6;
26867
+ const padY = 5;
26868
+ const bw = valueText.length * VALUE_FONT_SIZE * CHAR_WIDTH_RATIO2 + 8;
26869
+ const bh = VALUE_FONT_SIZE + 4;
26870
+ const bx = Math.max(-ln.width / 2 + 4, ln.width / 2 - bw - 4);
26871
+ const by = -ln.height / 2 + 4;
26872
+ nodeG.append("rect").attr("x", bx).attr("y", by).attr("width", bw).attr("height", bh).attr("rx", 3).attr("fill", palette.bg).attr("opacity", 0.85);
26873
+ nodeG.append("text").attr("class", "bl-node-value").attr("x", bx + bw - padX).attr("y", by + padY).attr("text-anchor", "end").attr("dominant-baseline", "central").attr("font-size", VALUE_FONT_SIZE).attr("font-weight", "600").attr("fill", palette.textMuted).text(valueText);
26863
26874
  }
26864
26875
  }
26865
26876
  const hasDescriptions = parsed.nodes.some(
@@ -26955,7 +26966,7 @@ var init_renderer6 = __esm({
26955
26966
  init_wrapped_desc();
26956
26967
  init_scaling();
26957
26968
  DIAGRAM_PADDING6 = 20;
26958
- NODE_FONT_SIZE = 13;
26969
+ NODE_FONT_SIZE = 11;
26959
26970
  MIN_NODE_FONT_SIZE = 9;
26960
26971
  EDGE_LABEL_FONT_SIZE4 = 11;
26961
26972
  EDGE_STROKE_WIDTH5 = 1.5;
@@ -46692,7 +46703,11 @@ function resolveMap(parsed, data) {
46692
46703
  if (bb && !isWholeSphere(bb)) containerBoxes.push(bb);
46693
46704
  }
46694
46705
  const containerUnion = unionExtent(containerBoxes, points);
46695
- if (containerUnion) extent2 = pad(containerUnion, PAD_FRACTION);
46706
+ if (containerUnion)
46707
+ extent2 = pad(
46708
+ clampContainerToCluster(containerUnion, points),
46709
+ PAD_FRACTION
46710
+ );
46696
46711
  }
46697
46712
  if (isPoiOnly) {
46698
46713
  const cx = (extent2[0][0] + extent2[1][0]) / 2;
@@ -46773,6 +46788,22 @@ function mostCommonCountry(regions, poiCountries) {
46773
46788
  }
46774
46789
  return best;
46775
46790
  }
46791
+ function clampContainerToCluster(container, points) {
46792
+ const poi = unionExtent([], points);
46793
+ if (!poi) return container;
46794
+ let [[west, south], [east, north]] = container;
46795
+ const [[pWest, pSouth], [pEast, pNorth]] = poi;
46796
+ south = Math.max(south, pSouth - CONTAINER_OVERSHOOT_DEG);
46797
+ north = Math.min(north, pNorth + CONTAINER_OVERSHOOT_DEG);
46798
+ if (east <= 180 && pEast <= 180) {
46799
+ west = Math.max(west, pWest - CONTAINER_OVERSHOOT_DEG);
46800
+ east = Math.min(east, pEast + CONTAINER_OVERSHOOT_DEG);
46801
+ }
46802
+ return [
46803
+ [west, south],
46804
+ [east, north]
46805
+ ];
46806
+ }
46776
46807
  function pad(e, frac) {
46777
46808
  const dLon = (e[1][0] - e[0][0]) * frac || 1;
46778
46809
  const dLat = (e[1][1] - e[0][1]) * frac || 1;
@@ -46785,7 +46816,7 @@ function firstError(diags) {
46785
46816
  const e = diags.find((d) => d.severity === "error");
46786
46817
  return e ? formatDgmoError(e) : null;
46787
46818
  }
46788
- var WORLD_SPAN, MERCATOR_MAX_LAT, PAD_FRACTION, REGION_PAD_FRACTION, WORLD_LAT_SOUTH, WORLD_LAT_NORTH, POI_ZOOM_FLOOR_DEG, US_NATIONAL_LON_SPAN, REGION_ALIASES, US_STATE_POSTAL;
46819
+ var WORLD_SPAN, MERCATOR_MAX_LAT, PAD_FRACTION, REGION_PAD_FRACTION, WORLD_LAT_SOUTH, WORLD_LAT_NORTH, POI_ZOOM_FLOOR_DEG, CONTAINER_OVERSHOOT_DEG, US_NATIONAL_LON_SPAN, REGION_ALIASES, US_STATE_POSTAL;
46789
46820
  var init_resolver2 = __esm({
46790
46821
  "src/map/resolver.ts"() {
46791
46822
  "use strict";
@@ -46798,6 +46829,7 @@ var init_resolver2 = __esm({
46798
46829
  WORLD_LAT_SOUTH = -58;
46799
46830
  WORLD_LAT_NORTH = 78;
46800
46831
  POI_ZOOM_FLOOR_DEG = 7;
46832
+ CONTAINER_OVERSHOOT_DEG = 8;
46801
46833
  US_NATIONAL_LON_SPAN = 48;
46802
46834
  REGION_ALIASES = {
46803
46835
  // Common everyday names → the Natural-Earth display name actually shipped.
@@ -46876,6 +46908,55 @@ var init_resolver2 = __esm({
46876
46908
  }
46877
46909
  });
46878
46910
 
46911
+ // src/map/legend-band.ts
46912
+ function mapLegendGroups(legend) {
46913
+ const ramp = legend.ramp;
46914
+ const scoreGroup = ramp ? {
46915
+ name: ramp.metric?.trim() || "Value",
46916
+ entries: [],
46917
+ gradient: {
46918
+ min: ramp.min,
46919
+ max: ramp.max,
46920
+ hue: ramp.hue,
46921
+ base: ramp.base
46922
+ }
46923
+ } : null;
46924
+ const tagGroups = legend.tagGroups.filter((g) => g.entries.length > 0).map((g) => ({ name: g.name, entries: [...g.entries] }));
46925
+ return [...scoreGroup ? [scoreGroup] : [], ...tagGroups];
46926
+ }
46927
+ function mapLegendConfig(groups, mode) {
46928
+ return {
46929
+ groups,
46930
+ position: { placement: "top-center", titleRelation: "below-title" },
46931
+ mode,
46932
+ showEmptyGroups: false,
46933
+ showInactivePills: true
46934
+ };
46935
+ }
46936
+ function mapLegendTop(hasTitle, hasSubtitle) {
46937
+ return (hasTitle ? TITLE_Y + TITLE_FONT_SIZE : 0) + (hasSubtitle ? TITLE_FONT_SIZE : 0) + LEGEND_TOP_GAP2;
46938
+ }
46939
+ function mapLegendBand(legend, opts) {
46940
+ if (!legend) return 0;
46941
+ const groups = mapLegendGroups(legend);
46942
+ if (groups.length === 0) return 0;
46943
+ const config = mapLegendConfig(groups, opts.mode);
46944
+ const state = { activeGroup: legend.activeGroup };
46945
+ const { height } = computeLegendLayout(config, state, opts.width);
46946
+ if (height <= 0) return 0;
46947
+ return mapLegendTop(opts.hasTitle, opts.hasSubtitle) + height + LEGEND_BOTTOM_GAP2;
46948
+ }
46949
+ var LEGEND_TOP_GAP2, LEGEND_BOTTOM_GAP2;
46950
+ var init_legend_band = __esm({
46951
+ "src/map/legend-band.ts"() {
46952
+ "use strict";
46953
+ init_legend_layout();
46954
+ init_title_constants();
46955
+ LEGEND_TOP_GAP2 = 8;
46956
+ LEGEND_BOTTOM_GAP2 = 10;
46957
+ }
46958
+ });
46959
+
46879
46960
  // src/map/colorize.ts
46880
46961
  function assignColors(isos, adjacency) {
46881
46962
  const sorted = [...isos].sort();
@@ -47456,12 +47537,43 @@ function layoutMap(resolved, data, size, opts) {
47456
47537
  return tagFill(r.tags, activeGroup) ?? neutralFill;
47457
47538
  };
47458
47539
  const regionById = new Map(resolved.regions.map((r) => [r.iso, r]));
47540
+ let legend = null;
47541
+ if (!resolved.directives.noLegend) {
47542
+ const legendTagGroups = resolved.tagGroups.map((g) => ({
47543
+ name: g.name,
47544
+ entries: g.entries.map((e) => ({ value: e.value, color: e.color }))
47545
+ }));
47546
+ if (legendTagGroups.length > 0 || hasRamp) {
47547
+ legend = {
47548
+ tagGroups: legendTagGroups,
47549
+ activeGroup,
47550
+ ...hasRamp && {
47551
+ ramp: {
47552
+ ...resolved.directives.regionMetric !== void 0 && {
47553
+ metric: resolved.directives.regionMetric
47554
+ },
47555
+ min: rampMin,
47556
+ max: rampMax,
47557
+ hue: rampHue,
47558
+ base: rampBase
47559
+ }
47560
+ }
47561
+ };
47562
+ }
47563
+ }
47459
47564
  const TITLE_GAP2 = 16;
47460
47565
  let topPad = FIT_PAD;
47461
47566
  if (resolved.title && resolved.pois.length > 0) {
47462
47567
  const bannerBottom = (resolved.subtitle ? TITLE_Y + TITLE_FONT_SIZE : TITLE_Y) + TITLE_FONT_SIZE / 2;
47463
47568
  topPad = Math.max(FIT_PAD, bannerBottom + TITLE_GAP2);
47464
47569
  }
47570
+ const legendBand = mapLegendBand(legend, {
47571
+ width,
47572
+ mode: opts.legendMode ?? "preview",
47573
+ hasTitle: Boolean(resolved.title),
47574
+ hasSubtitle: Boolean(resolved.subtitle)
47575
+ });
47576
+ if (legendBand > topPad) topPad = legendBand;
47465
47577
  const fitBox = [
47466
47578
  [FIT_PAD, topPad],
47467
47579
  [
@@ -47479,7 +47591,7 @@ function layoutMap(resolved, data, size, opts) {
47479
47591
  const by0 = cb[0][1];
47480
47592
  const cw = cb[1][0] - bx0;
47481
47593
  const ch = cb[1][1] - by0;
47482
- const topReserve = resolved.title && resolved.pois.length > 0 ? topPad : 0;
47594
+ const topReserve = resolved.title && resolved.pois.length > 0 || legendBand > 0 ? topPad : 0;
47483
47595
  const ox = 0;
47484
47596
  const oy = topReserve;
47485
47597
  const sx = cw > 0 ? width / cw : 1;
@@ -48543,30 +48655,6 @@ function layoutMap(resolved, data, size, opts) {
48543
48655
  });
48544
48656
  labels.push(...contextLabels);
48545
48657
  }
48546
- let legend = null;
48547
- if (!resolved.directives.noLegend) {
48548
- const tagGroups = resolved.tagGroups.map((g) => ({
48549
- name: g.name,
48550
- entries: g.entries.map((e) => ({ value: e.value, color: e.color }))
48551
- }));
48552
- if (tagGroups.length > 0 || hasRamp) {
48553
- legend = {
48554
- tagGroups,
48555
- activeGroup,
48556
- ...hasRamp && {
48557
- ramp: {
48558
- ...resolved.directives.regionMetric !== void 0 && {
48559
- metric: resolved.directives.regionMetric
48560
- },
48561
- min: rampMin,
48562
- max: rampMax,
48563
- hue: rampHue,
48564
- base: rampBase
48565
- }
48566
- }
48567
- };
48568
- }
48569
- }
48570
48658
  return {
48571
48659
  width,
48572
48660
  height,
@@ -48604,6 +48692,7 @@ var init_layout15 = __esm({
48604
48692
  init_label_layout();
48605
48693
  init_legend_constants();
48606
48694
  init_title_constants();
48695
+ init_legend_band();
48607
48696
  init_context_labels();
48608
48697
  FIT_PAD = 24;
48609
48698
  RAMP_FLOOR2 = 15;
@@ -48803,6 +48892,9 @@ function renderMap(container, resolved, data, palette, isDark, onClickItem, expo
48803
48892
  // stretch-distorting. The in-app preview pane passes no exportDims → unset →
48804
48893
  // keeps the global stretch-fill.
48805
48894
  preferContain: exportDims?.preferContain ?? false,
48895
+ // Reserve the legend band for the mode actually drawn below (export shows
48896
+ // only the active group; preview keeps the inactive pills).
48897
+ legendMode: exportDims ? "export" : "preview",
48806
48898
  ...activeGroupOverride !== void 0 && {
48807
48899
  activeGroup: activeGroupOverride
48808
48900
  }
@@ -49094,30 +49186,12 @@ function renderMap(container, resolved, data, palette, isDark, onClickItem, expo
49094
49186
  if (layout.legend) {
49095
49187
  const legendY = (layout.title ? TITLE_Y + TITLE_FONT_SIZE : 0) + (layout.subtitle ? TITLE_FONT_SIZE : 0) + 8;
49096
49188
  const legendG = svg.append("g").attr("class", "dgmo-map-legend").attr("transform", `translate(0, ${legendY})`);
49097
- const ramp = layout.legend.ramp;
49098
- const scoreGroup = ramp ? {
49099
- name: ramp.metric?.trim() || "Value",
49100
- entries: [],
49101
- gradient: {
49102
- min: ramp.min,
49103
- max: ramp.max,
49104
- hue: ramp.hue,
49105
- base: ramp.base
49106
- }
49107
- } : null;
49108
- const tagGroups = layout.legend.tagGroups.filter((g) => g.entries.length > 0).map((g) => ({ name: g.name, entries: [...g.entries] }));
49109
- const groups = [...scoreGroup ? [scoreGroup] : [], ...tagGroups];
49189
+ const groups = mapLegendGroups(layout.legend);
49110
49190
  if (groups.length > 0) {
49111
- const config = {
49191
+ const config = mapLegendConfig(
49112
49192
  groups,
49113
- position: { placement: "top-center", titleRelation: "below-title" },
49114
- mode: exportDims ? "export" : "preview",
49115
- showEmptyGroups: false,
49116
- // Keep inactive siblings visible as pills so the user can click to flip
49117
- // the active colouring dimension (preview only — export shows just the
49118
- // active group).
49119
- showInactivePills: true
49120
- };
49193
+ exportDims ? "export" : "preview"
49194
+ );
49121
49195
  const state = { activeGroup: layout.legend.activeGroup };
49122
49196
  renderLegendD3(legendG, config, state, palette, isDark, void 0, width);
49123
49197
  }
@@ -49162,6 +49236,7 @@ var init_renderer16 = __esm({
49162
49236
  init_title_constants();
49163
49237
  init_color_utils();
49164
49238
  init_legend_d3();
49239
+ init_legend_band();
49165
49240
  init_layout15();
49166
49241
  LABEL_FONT = 11;
49167
49242
  }
@@ -49182,9 +49257,10 @@ function mapContentAspect(resolved, data, ref = REF) {
49182
49257
  const aspect = w / h;
49183
49258
  return Number.isFinite(aspect) && aspect > 0 ? aspect : FALLBACK_ASPECT;
49184
49259
  }
49185
- function mapExportDimensions(resolved, data, baseWidth = 1200) {
49186
- const raw = mapContentAspect(resolved, data);
49187
- const clamped = Math.max(ASPECT_MIN, Math.min(ASPECT_MAX, raw));
49260
+ function mapExportDimensions(resolved, data, baseWidth = 1200, aspectOverride) {
49261
+ const useOverride = aspectOverride !== void 0 && Number.isFinite(aspectOverride) && aspectOverride > 0;
49262
+ const raw = useOverride ? aspectOverride : mapContentAspect(resolved, data);
49263
+ const clamped = useOverride ? raw : Math.max(ASPECT_MIN, Math.min(ASPECT_MAX, raw));
49188
49264
  const width = baseWidth;
49189
49265
  let height = Math.round(width / clamped);
49190
49266
  let chromeReserve = 0;
@@ -49197,7 +49273,7 @@ function mapExportDimensions(resolved, data, baseWidth = 1200) {
49197
49273
  height = Math.round(chromeReserve + MIN_MAP_BAND);
49198
49274
  floored = true;
49199
49275
  }
49200
- const preferContain = clamped !== raw || floored;
49276
+ const preferContain = useOverride ? floored : clamped !== raw || floored;
49201
49277
  return { width, height, preferContain };
49202
49278
  }
49203
49279
  var import_d3_geo3, FIT_PAD2, TITLE_GAP, ASPECT_MAX, ASPECT_MIN, MIN_MAP_BAND, FALLBACK_ASPECT, REF;
@@ -54904,7 +54980,6 @@ function renderTimelineTagLegendOverlay(container, parsed, palette, isDark, setu
54904
54980
  function renderTimelineHorizontalTimeSort(container, parsed, palette, isDark, setup, hovers, onClickItem, _exportDims, _swimlaneTagGroup, _activeTagGroup, _onTagStateChange, _viewMode) {
54905
54981
  const {
54906
54982
  width,
54907
- height,
54908
54983
  tooltip,
54909
54984
  solid,
54910
54985
  textColor,
@@ -54953,8 +55028,7 @@ function renderTimelineHorizontalTimeSort(container, parsed, palette, isDark, se
54953
55028
  const markerLabelY = markerReserve ? -(topScaleH + MARKER_ROW_H / 2) : 0;
54954
55029
  const eraLabelY = eraReserve ? -(topScaleH + markerReserve + ERA_ROW_H / 2) : 0;
54955
55030
  const innerWidth = width - margin.left - margin.right;
54956
- const availInnerHeight = height - margin.top - margin.bottom;
54957
- const rowH = Math.min(ctx.structural(28), availInnerHeight / sorted.length);
55031
+ const rowH = ctx.structural(28);
54958
55032
  const innerHeight = rowH * sorted.length;
54959
55033
  const usedHeight = margin.top + innerHeight + margin.bottom;
54960
55034
  const xScale = d3Scale2.scaleLinear().domain([minDate - datePadding, maxDate + datePadding]).range([0, innerWidth]);
@@ -57479,7 +57553,12 @@ async function renderForExport(content, theme, palette, viewState, options) {
57479
57553
  }
57480
57554
  }
57481
57555
  const mapResolved = resolveMap2(mapParsed, mapData);
57482
- const dims2 = mapExportDimensions2(mapResolved, mapData, EXPORT_WIDTH);
57556
+ const dims2 = mapExportDimensions2(
57557
+ mapResolved,
57558
+ mapData,
57559
+ EXPORT_WIDTH,
57560
+ options?.mapAspect
57561
+ );
57483
57562
  const container2 = createExportContainer(dims2.width, dims2.height);
57484
57563
  renderMapForExport2(
57485
57564
  container2,
@@ -59214,7 +59293,7 @@ pre.dgmo, code.language-dgmo, pre > code.language-dgmo,
59214
59293
 
59215
59294
  // src/auto/index.ts
59216
59295
  init_safe_href();
59217
- var VERSION = "0.23.0";
59296
+ var VERSION = "0.24.0";
59218
59297
  var DEFAULTS = {
59219
59298
  theme: "auto",
59220
59299
  palette: "nord",