@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.mjs CHANGED
@@ -26510,7 +26510,7 @@ function renderBoxesAndLines(container, parsed, layout, palette, isDark, options
26510
26510
  const VALUE_NAME = hasRamp ? parsed.boxMetric?.trim() || "Value" : null;
26511
26511
  const matchColorGroup = (v) => {
26512
26512
  const lv = v.trim().toLowerCase();
26513
- if (lv === "none") return null;
26513
+ if (lv === "" || lv === "none") return null;
26514
26514
  const tg = parsed.tagGroups.find((g) => g.name.toLowerCase() === lv);
26515
26515
  if (tg) return tg.name;
26516
26516
  if (lv === VALUE_NAME?.toLowerCase()) return VALUE_NAME;
@@ -26851,6 +26851,22 @@ function renderBoxesAndLines(container, parsed, layout, palette, isDark, options
26851
26851
  const tooltipText = fullText.length > 200 ? fullText.slice(0, 199) + "\u2026" : fullText;
26852
26852
  nodeG.append("title").text(tooltipText);
26853
26853
  }
26854
+ } else if (parsed.showValues && node.value !== void 0) {
26855
+ const valueLabel = parsed.boxMetric ? `${parsed.boxMetric}: ${node.value}` : String(node.value);
26856
+ const headerH = ln.height / 2;
26857
+ const sepY = -ln.height / 2 + headerH;
26858
+ const fitted = fitLabelToHeader(node.label, ln.width, 2);
26859
+ const labelLineH = fitted.fontSize * 1.3;
26860
+ const labelTotalH = fitted.lines.length * labelLineH;
26861
+ const headerCenterY = -ln.height / 2 + headerH / 2;
26862
+ for (let li = 0; li < fitted.lines.length; li++) {
26863
+ nodeG.append("text").attr("x", 0).attr(
26864
+ "y",
26865
+ headerCenterY - labelTotalH / 2 + labelLineH / 2 + li * labelLineH
26866
+ ).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]);
26867
+ }
26868
+ 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);
26869
+ 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);
26854
26870
  } else {
26855
26871
  const maxLabelLines = Math.max(
26856
26872
  2,
@@ -26863,21 +26879,16 @@ function renderBoxesAndLines(container, parsed, layout, palette, isDark, options
26863
26879
  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]);
26864
26880
  }
26865
26881
  }
26866
- if (parsed.showValues && node.value !== void 0) {
26882
+ if (parsed.showValues && node.value !== void 0 && desc && desc.length > 0 && !hideDescriptions) {
26867
26883
  const valueText = String(node.value);
26868
- const descShown = !!(desc && desc.length > 0 && !hideDescriptions);
26869
- if (descShown) {
26870
- const padX = 6;
26871
- const padY = 5;
26872
- const bw = valueText.length * VALUE_FONT_SIZE * CHAR_WIDTH_RATIO2 + 8;
26873
- const bh = VALUE_FONT_SIZE + 4;
26874
- const bx = ln.width / 2 - bw - 4;
26875
- const by = -ln.height / 2 + 4;
26876
- 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);
26877
- 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);
26878
- } else {
26879
- 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);
26880
- }
26884
+ const padX = 6;
26885
+ const padY = 5;
26886
+ const bw = valueText.length * VALUE_FONT_SIZE * CHAR_WIDTH_RATIO2 + 8;
26887
+ const bh = VALUE_FONT_SIZE + 4;
26888
+ const bx = Math.max(-ln.width / 2 + 4, ln.width / 2 - bw - 4);
26889
+ const by = -ln.height / 2 + 4;
26890
+ 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);
26891
+ 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);
26881
26892
  }
26882
26893
  }
26883
26894
  const hasDescriptions = parsed.nodes.some(
@@ -26971,7 +26982,7 @@ var init_renderer6 = __esm({
26971
26982
  init_wrapped_desc();
26972
26983
  init_scaling();
26973
26984
  DIAGRAM_PADDING6 = 20;
26974
- NODE_FONT_SIZE = 13;
26985
+ NODE_FONT_SIZE = 11;
26975
26986
  MIN_NODE_FONT_SIZE = 9;
26976
26987
  EDGE_LABEL_FONT_SIZE4 = 11;
26977
26988
  EDGE_STROKE_WIDTH5 = 1.5;
@@ -46708,7 +46719,11 @@ function resolveMap(parsed, data) {
46708
46719
  if (bb && !isWholeSphere(bb)) containerBoxes.push(bb);
46709
46720
  }
46710
46721
  const containerUnion = unionExtent(containerBoxes, points);
46711
- if (containerUnion) extent2 = pad(containerUnion, PAD_FRACTION);
46722
+ if (containerUnion)
46723
+ extent2 = pad(
46724
+ clampContainerToCluster(containerUnion, points),
46725
+ PAD_FRACTION
46726
+ );
46712
46727
  }
46713
46728
  if (isPoiOnly) {
46714
46729
  const cx = (extent2[0][0] + extent2[1][0]) / 2;
@@ -46789,6 +46804,22 @@ function mostCommonCountry(regions, poiCountries) {
46789
46804
  }
46790
46805
  return best;
46791
46806
  }
46807
+ function clampContainerToCluster(container, points) {
46808
+ const poi = unionExtent([], points);
46809
+ if (!poi) return container;
46810
+ let [[west, south], [east, north]] = container;
46811
+ const [[pWest, pSouth], [pEast, pNorth]] = poi;
46812
+ south = Math.max(south, pSouth - CONTAINER_OVERSHOOT_DEG);
46813
+ north = Math.min(north, pNorth + CONTAINER_OVERSHOOT_DEG);
46814
+ if (east <= 180 && pEast <= 180) {
46815
+ west = Math.max(west, pWest - CONTAINER_OVERSHOOT_DEG);
46816
+ east = Math.min(east, pEast + CONTAINER_OVERSHOOT_DEG);
46817
+ }
46818
+ return [
46819
+ [west, south],
46820
+ [east, north]
46821
+ ];
46822
+ }
46792
46823
  function pad(e, frac) {
46793
46824
  const dLon = (e[1][0] - e[0][0]) * frac || 1;
46794
46825
  const dLat = (e[1][1] - e[0][1]) * frac || 1;
@@ -46801,7 +46832,7 @@ function firstError(diags) {
46801
46832
  const e = diags.find((d) => d.severity === "error");
46802
46833
  return e ? formatDgmoError(e) : null;
46803
46834
  }
46804
- 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;
46835
+ 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;
46805
46836
  var init_resolver2 = __esm({
46806
46837
  "src/map/resolver.ts"() {
46807
46838
  "use strict";
@@ -46814,6 +46845,7 @@ var init_resolver2 = __esm({
46814
46845
  WORLD_LAT_SOUTH = -58;
46815
46846
  WORLD_LAT_NORTH = 78;
46816
46847
  POI_ZOOM_FLOOR_DEG = 7;
46848
+ CONTAINER_OVERSHOOT_DEG = 8;
46817
46849
  US_NATIONAL_LON_SPAN = 48;
46818
46850
  REGION_ALIASES = {
46819
46851
  // Common everyday names → the Natural-Earth display name actually shipped.
@@ -46892,6 +46924,55 @@ var init_resolver2 = __esm({
46892
46924
  }
46893
46925
  });
46894
46926
 
46927
+ // src/map/legend-band.ts
46928
+ function mapLegendGroups(legend) {
46929
+ const ramp = legend.ramp;
46930
+ const scoreGroup = ramp ? {
46931
+ name: ramp.metric?.trim() || "Value",
46932
+ entries: [],
46933
+ gradient: {
46934
+ min: ramp.min,
46935
+ max: ramp.max,
46936
+ hue: ramp.hue,
46937
+ base: ramp.base
46938
+ }
46939
+ } : null;
46940
+ const tagGroups = legend.tagGroups.filter((g) => g.entries.length > 0).map((g) => ({ name: g.name, entries: [...g.entries] }));
46941
+ return [...scoreGroup ? [scoreGroup] : [], ...tagGroups];
46942
+ }
46943
+ function mapLegendConfig(groups, mode) {
46944
+ return {
46945
+ groups,
46946
+ position: { placement: "top-center", titleRelation: "below-title" },
46947
+ mode,
46948
+ showEmptyGroups: false,
46949
+ showInactivePills: true
46950
+ };
46951
+ }
46952
+ function mapLegendTop(hasTitle, hasSubtitle) {
46953
+ return (hasTitle ? TITLE_Y + TITLE_FONT_SIZE : 0) + (hasSubtitle ? TITLE_FONT_SIZE : 0) + LEGEND_TOP_GAP2;
46954
+ }
46955
+ function mapLegendBand(legend, opts) {
46956
+ if (!legend) return 0;
46957
+ const groups = mapLegendGroups(legend);
46958
+ if (groups.length === 0) return 0;
46959
+ const config = mapLegendConfig(groups, opts.mode);
46960
+ const state = { activeGroup: legend.activeGroup };
46961
+ const { height } = computeLegendLayout(config, state, opts.width);
46962
+ if (height <= 0) return 0;
46963
+ return mapLegendTop(opts.hasTitle, opts.hasSubtitle) + height + LEGEND_BOTTOM_GAP2;
46964
+ }
46965
+ var LEGEND_TOP_GAP2, LEGEND_BOTTOM_GAP2;
46966
+ var init_legend_band = __esm({
46967
+ "src/map/legend-band.ts"() {
46968
+ "use strict";
46969
+ init_legend_layout();
46970
+ init_title_constants();
46971
+ LEGEND_TOP_GAP2 = 8;
46972
+ LEGEND_BOTTOM_GAP2 = 10;
46973
+ }
46974
+ });
46975
+
46895
46976
  // src/map/colorize.ts
46896
46977
  function assignColors(isos, adjacency) {
46897
46978
  const sorted = [...isos].sort();
@@ -47483,12 +47564,43 @@ function layoutMap(resolved, data, size, opts) {
47483
47564
  return tagFill(r.tags, activeGroup) ?? neutralFill;
47484
47565
  };
47485
47566
  const regionById = new Map(resolved.regions.map((r) => [r.iso, r]));
47567
+ let legend = null;
47568
+ if (!resolved.directives.noLegend) {
47569
+ const legendTagGroups = resolved.tagGroups.map((g) => ({
47570
+ name: g.name,
47571
+ entries: g.entries.map((e) => ({ value: e.value, color: e.color }))
47572
+ }));
47573
+ if (legendTagGroups.length > 0 || hasRamp) {
47574
+ legend = {
47575
+ tagGroups: legendTagGroups,
47576
+ activeGroup,
47577
+ ...hasRamp && {
47578
+ ramp: {
47579
+ ...resolved.directives.regionMetric !== void 0 && {
47580
+ metric: resolved.directives.regionMetric
47581
+ },
47582
+ min: rampMin,
47583
+ max: rampMax,
47584
+ hue: rampHue,
47585
+ base: rampBase
47586
+ }
47587
+ }
47588
+ };
47589
+ }
47590
+ }
47486
47591
  const TITLE_GAP2 = 16;
47487
47592
  let topPad = FIT_PAD;
47488
47593
  if (resolved.title && resolved.pois.length > 0) {
47489
47594
  const bannerBottom = (resolved.subtitle ? TITLE_Y + TITLE_FONT_SIZE : TITLE_Y) + TITLE_FONT_SIZE / 2;
47490
47595
  topPad = Math.max(FIT_PAD, bannerBottom + TITLE_GAP2);
47491
47596
  }
47597
+ const legendBand = mapLegendBand(legend, {
47598
+ width,
47599
+ mode: opts.legendMode ?? "preview",
47600
+ hasTitle: Boolean(resolved.title),
47601
+ hasSubtitle: Boolean(resolved.subtitle)
47602
+ });
47603
+ if (legendBand > topPad) topPad = legendBand;
47492
47604
  const fitBox = [
47493
47605
  [FIT_PAD, topPad],
47494
47606
  [
@@ -47506,7 +47618,7 @@ function layoutMap(resolved, data, size, opts) {
47506
47618
  const by0 = cb[0][1];
47507
47619
  const cw = cb[1][0] - bx0;
47508
47620
  const ch = cb[1][1] - by0;
47509
- const topReserve = resolved.title && resolved.pois.length > 0 ? topPad : 0;
47621
+ const topReserve = resolved.title && resolved.pois.length > 0 || legendBand > 0 ? topPad : 0;
47510
47622
  const ox = 0;
47511
47623
  const oy = topReserve;
47512
47624
  const sx = cw > 0 ? width / cw : 1;
@@ -48570,30 +48682,6 @@ function layoutMap(resolved, data, size, opts) {
48570
48682
  });
48571
48683
  labels.push(...contextLabels);
48572
48684
  }
48573
- let legend = null;
48574
- if (!resolved.directives.noLegend) {
48575
- const tagGroups = resolved.tagGroups.map((g) => ({
48576
- name: g.name,
48577
- entries: g.entries.map((e) => ({ value: e.value, color: e.color }))
48578
- }));
48579
- if (tagGroups.length > 0 || hasRamp) {
48580
- legend = {
48581
- tagGroups,
48582
- activeGroup,
48583
- ...hasRamp && {
48584
- ramp: {
48585
- ...resolved.directives.regionMetric !== void 0 && {
48586
- metric: resolved.directives.regionMetric
48587
- },
48588
- min: rampMin,
48589
- max: rampMax,
48590
- hue: rampHue,
48591
- base: rampBase
48592
- }
48593
- }
48594
- };
48595
- }
48596
- }
48597
48685
  return {
48598
48686
  width,
48599
48687
  height,
@@ -48629,6 +48717,7 @@ var init_layout15 = __esm({
48629
48717
  init_label_layout();
48630
48718
  init_legend_constants();
48631
48719
  init_title_constants();
48720
+ init_legend_band();
48632
48721
  init_context_labels();
48633
48722
  FIT_PAD = 24;
48634
48723
  RAMP_FLOOR2 = 15;
@@ -48829,6 +48918,9 @@ function renderMap(container, resolved, data, palette, isDark, onClickItem, expo
48829
48918
  // stretch-distorting. The in-app preview pane passes no exportDims → unset →
48830
48919
  // keeps the global stretch-fill.
48831
48920
  preferContain: exportDims?.preferContain ?? false,
48921
+ // Reserve the legend band for the mode actually drawn below (export shows
48922
+ // only the active group; preview keeps the inactive pills).
48923
+ legendMode: exportDims ? "export" : "preview",
48832
48924
  ...activeGroupOverride !== void 0 && {
48833
48925
  activeGroup: activeGroupOverride
48834
48926
  }
@@ -49120,30 +49212,12 @@ function renderMap(container, resolved, data, palette, isDark, onClickItem, expo
49120
49212
  if (layout.legend) {
49121
49213
  const legendY = (layout.title ? TITLE_Y + TITLE_FONT_SIZE : 0) + (layout.subtitle ? TITLE_FONT_SIZE : 0) + 8;
49122
49214
  const legendG = svg.append("g").attr("class", "dgmo-map-legend").attr("transform", `translate(0, ${legendY})`);
49123
- const ramp = layout.legend.ramp;
49124
- const scoreGroup = ramp ? {
49125
- name: ramp.metric?.trim() || "Value",
49126
- entries: [],
49127
- gradient: {
49128
- min: ramp.min,
49129
- max: ramp.max,
49130
- hue: ramp.hue,
49131
- base: ramp.base
49132
- }
49133
- } : null;
49134
- const tagGroups = layout.legend.tagGroups.filter((g) => g.entries.length > 0).map((g) => ({ name: g.name, entries: [...g.entries] }));
49135
- const groups = [...scoreGroup ? [scoreGroup] : [], ...tagGroups];
49215
+ const groups = mapLegendGroups(layout.legend);
49136
49216
  if (groups.length > 0) {
49137
- const config = {
49217
+ const config = mapLegendConfig(
49138
49218
  groups,
49139
- position: { placement: "top-center", titleRelation: "below-title" },
49140
- mode: exportDims ? "export" : "preview",
49141
- showEmptyGroups: false,
49142
- // Keep inactive siblings visible as pills so the user can click to flip
49143
- // the active colouring dimension (preview only — export shows just the
49144
- // active group).
49145
- showInactivePills: true
49146
- };
49219
+ exportDims ? "export" : "preview"
49220
+ );
49147
49221
  const state = { activeGroup: layout.legend.activeGroup };
49148
49222
  renderLegendD3(legendG, config, state, palette, isDark, void 0, width);
49149
49223
  }
@@ -49187,6 +49261,7 @@ var init_renderer16 = __esm({
49187
49261
  init_title_constants();
49188
49262
  init_color_utils();
49189
49263
  init_legend_d3();
49264
+ init_legend_band();
49190
49265
  init_layout15();
49191
49266
  LABEL_FONT = 11;
49192
49267
  }
@@ -49208,9 +49283,10 @@ function mapContentAspect(resolved, data, ref = REF) {
49208
49283
  const aspect = w / h;
49209
49284
  return Number.isFinite(aspect) && aspect > 0 ? aspect : FALLBACK_ASPECT;
49210
49285
  }
49211
- function mapExportDimensions(resolved, data, baseWidth = 1200) {
49212
- const raw = mapContentAspect(resolved, data);
49213
- const clamped = Math.max(ASPECT_MIN, Math.min(ASPECT_MAX, raw));
49286
+ function mapExportDimensions(resolved, data, baseWidth = 1200, aspectOverride) {
49287
+ const useOverride = aspectOverride !== void 0 && Number.isFinite(aspectOverride) && aspectOverride > 0;
49288
+ const raw = useOverride ? aspectOverride : mapContentAspect(resolved, data);
49289
+ const clamped = useOverride ? raw : Math.max(ASPECT_MIN, Math.min(ASPECT_MAX, raw));
49214
49290
  const width = baseWidth;
49215
49291
  let height = Math.round(width / clamped);
49216
49292
  let chromeReserve = 0;
@@ -49223,7 +49299,7 @@ function mapExportDimensions(resolved, data, baseWidth = 1200) {
49223
49299
  height = Math.round(chromeReserve + MIN_MAP_BAND);
49224
49300
  floored = true;
49225
49301
  }
49226
- const preferContain = clamped !== raw || floored;
49302
+ const preferContain = useOverride ? floored : clamped !== raw || floored;
49227
49303
  return { width, height, preferContain };
49228
49304
  }
49229
49305
  var FIT_PAD2, TITLE_GAP, ASPECT_MAX, ASPECT_MIN, MIN_MAP_BAND, FALLBACK_ASPECT, REF;
@@ -54933,7 +55009,6 @@ function renderTimelineTagLegendOverlay(container, parsed, palette, isDark, setu
54933
55009
  function renderTimelineHorizontalTimeSort(container, parsed, palette, isDark, setup, hovers, onClickItem, _exportDims, _swimlaneTagGroup, _activeTagGroup, _onTagStateChange, _viewMode) {
54934
55010
  const {
54935
55011
  width,
54936
- height,
54937
55012
  tooltip,
54938
55013
  solid,
54939
55014
  textColor,
@@ -54982,8 +55057,7 @@ function renderTimelineHorizontalTimeSort(container, parsed, palette, isDark, se
54982
55057
  const markerLabelY = markerReserve ? -(topScaleH + MARKER_ROW_H / 2) : 0;
54983
55058
  const eraLabelY = eraReserve ? -(topScaleH + markerReserve + ERA_ROW_H / 2) : 0;
54984
55059
  const innerWidth = width - margin.left - margin.right;
54985
- const availInnerHeight = height - margin.top - margin.bottom;
54986
- const rowH = Math.min(ctx.structural(28), availInnerHeight / sorted.length);
55060
+ const rowH = ctx.structural(28);
54987
55061
  const innerHeight = rowH * sorted.length;
54988
55062
  const usedHeight = margin.top + innerHeight + margin.bottom;
54989
55063
  const xScale = d3Scale2.scaleLinear().domain([minDate - datePadding, maxDate + datePadding]).range([0, innerWidth]);
@@ -57508,7 +57582,12 @@ async function renderForExport(content, theme, palette, viewState, options) {
57508
57582
  }
57509
57583
  }
57510
57584
  const mapResolved = resolveMap2(mapParsed, mapData);
57511
- const dims2 = mapExportDimensions2(mapResolved, mapData, EXPORT_WIDTH);
57585
+ const dims2 = mapExportDimensions2(
57586
+ mapResolved,
57587
+ mapData,
57588
+ EXPORT_WIDTH,
57589
+ options?.mapAspect
57590
+ );
57512
57591
  const container2 = createExportContainer(dims2.width, dims2.height);
57513
57592
  renderMapForExport2(
57514
57593
  container2,
@@ -59224,7 +59303,7 @@ pre.dgmo, code.language-dgmo, pre > code.language-dgmo,
59224
59303
 
59225
59304
  // src/auto/index.ts
59226
59305
  init_safe_href();
59227
- var VERSION = "0.23.0";
59306
+ var VERSION = "0.24.0";
59228
59307
  var DEFAULTS = {
59229
59308
  theme: "auto",
59230
59309
  palette: "nord",