@diagrammo/dgmo 0.7.2 → 0.7.3

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@diagrammo/dgmo",
3
- "version": "0.7.2",
3
+ "version": "0.7.3",
4
4
  "description": "DGMO diagram markup language — parser, renderer, and color system",
5
5
  "license": "MIT",
6
6
  "type": "module",
package/src/d3.ts CHANGED
@@ -5816,6 +5816,8 @@ function finalizeSvgExport(
5816
5816
  }
5817
5817
  svgEl.setAttribute('xmlns', 'http://www.w3.org/2000/svg');
5818
5818
  svgEl.style.fontFamily = FONT_FAMILY;
5819
+ // Strip elements marked for export exclusion (e.g., inactive legend pills)
5820
+ svgEl.querySelectorAll('[data-export-ignore]').forEach((el) => el.remove());
5819
5821
  const svgHtml = svgEl.outerHTML;
5820
5822
  document.body.removeChild(container);
5821
5823
  if (options?.branding !== false) {
@@ -674,6 +674,11 @@ export function renderInitiativeStatus(
674
674
  .attr('data-legend-group', lg.key)
675
675
  .style('cursor', 'pointer');
676
676
 
677
+ // Mark inactive pills so exports can hide them
678
+ if (!isActive) {
679
+ gEl.attr('data-export-ignore', 'true');
680
+ }
681
+
677
682
  if (isActive) {
678
683
  // Outer capsule background
679
684
  gEl.append('rect')
@@ -724,18 +729,33 @@ export function renderInitiativeStatus(
724
729
  // Determine which values are hidden for this group
725
730
  const hiddenSet = !lg.isStatus ? hiddenTagValues?.get(lg.key) : undefined;
726
731
 
727
- let entryX = pillXOff + pillW + 4;
732
+ // Render each entry in its own <g> with local coordinates,
733
+ // positioned via transform so we can reflow after measuring.
734
+ const entryStartX = pillXOff + pillW + 4;
735
+ const entryData: { g: d3Selection.Selection<SVGGElement, unknown, null, undefined>; textEl: SVGTextElement; estimatedW: number }[] = [];
736
+ let estimatedX = entryStartX;
737
+
728
738
  for (const entry of lg.entries) {
729
739
  const isHidden = hiddenSet?.has(entry.value) ?? false;
740
+ const estimatedTextW = entry.label.length * LEGEND_ENTRY_FONT_W;
730
741
 
731
742
  const entryG = gEl.append('g')
732
743
  .attr('data-legend-entry', entry.value)
733
- .style('cursor', !lg.isStatus ? 'pointer' : 'default');
744
+ .attr('transform', `translate(${estimatedX}, 0)`)
745
+ .style('cursor', 'pointer');
746
+
747
+ // Transparent hit-area rect
748
+ const entryW = LEGEND_DOT_R * 2 + LEGEND_ENTRY_DOT_GAP + estimatedTextW + LEGEND_ENTRY_TRAIL;
749
+ entryG.append('rect')
750
+ .attr('x', -2)
751
+ .attr('y', 0)
752
+ .attr('width', entryW + 4)
753
+ .attr('height', LEGEND_HEIGHT)
754
+ .attr('fill', 'transparent');
734
755
 
735
756
  if (isHidden) {
736
- // Hidden: hollow ring + dimmed text (strikethrough-like)
737
757
  entryG.append('circle')
738
- .attr('cx', entryX + LEGEND_DOT_R)
758
+ .attr('cx', LEGEND_DOT_R)
739
759
  .attr('cy', LEGEND_HEIGHT / 2)
740
760
  .attr('r', LEGEND_DOT_R)
741
761
  .attr('fill', 'none')
@@ -743,16 +763,15 @@ export function renderInitiativeStatus(
743
763
  .attr('stroke-width', 1.2)
744
764
  .attr('opacity', 0.5);
745
765
  } else {
746
- // Visible: solid dot
747
766
  entryG.append('circle')
748
- .attr('cx', entryX + LEGEND_DOT_R)
767
+ .attr('cx', LEGEND_DOT_R)
749
768
  .attr('cy', LEGEND_HEIGHT / 2)
750
769
  .attr('r', LEGEND_DOT_R)
751
770
  .attr('fill', entry.color);
752
771
  }
753
772
 
754
- entryG.append('text')
755
- .attr('x', entryX + LEGEND_DOT_R * 2 + LEGEND_ENTRY_DOT_GAP)
773
+ const textEl = entryG.append('text')
774
+ .attr('x', LEGEND_DOT_R * 2 + LEGEND_ENTRY_DOT_GAP)
756
775
  .attr('y', LEGEND_HEIGHT / 2 + LEGEND_ENTRY_FONT_SIZE / 2 - 1)
757
776
  .attr('font-size', LEGEND_ENTRY_FONT_SIZE)
758
777
  .attr('fill', palette.textMuted)
@@ -761,7 +780,20 @@ export function renderInitiativeStatus(
761
780
  .attr('text-decoration', isHidden ? 'line-through' : 'none')
762
781
  .text(entry.label);
763
782
 
764
- entryX += LEGEND_DOT_R * 2 + LEGEND_ENTRY_DOT_GAP + entry.label.length * LEGEND_ENTRY_FONT_W + LEGEND_ENTRY_TRAIL;
783
+ entryData.push({ g: entryG, textEl: textEl.node()!, estimatedW: estimatedTextW });
784
+ estimatedX += LEGEND_DOT_R * 2 + LEGEND_ENTRY_DOT_GAP + estimatedTextW + LEGEND_ENTRY_TRAIL;
785
+ }
786
+
787
+ // Reflow using measured text widths for even spacing
788
+ let reflowX = entryStartX;
789
+ for (const ed of entryData) {
790
+ const measuredW = ed.textEl.getComputedTextLength?.() ?? 0;
791
+ const textW = measuredW > 0 ? measuredW : ed.estimatedW;
792
+ ed.g.attr('transform', `translate(${reflowX}, 0)`);
793
+ // Update hit-area rect width to match actual width
794
+ const actualEntryW = LEGEND_DOT_R * 2 + LEGEND_ENTRY_DOT_GAP + textW + LEGEND_ENTRY_TRAIL;
795
+ ed.g.select('rect').attr('width', actualEntryW + 4);
796
+ reflowX += actualEntryW;
765
797
  }
766
798
  }
767
799