@diagrammo/dgmo 0.8.8 → 0.8.10
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/AGENTS.md +3 -0
- package/dist/cli.cjs +181 -179
- package/dist/index.cjs +1425 -933
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +147 -1
- package/dist/index.d.ts +147 -1
- package/dist/index.js +1421 -933
- package/dist/index.js.map +1 -1
- package/docs/language-reference.md +28 -2
- package/gallery/fixtures/sitemap-full.dgmo +1 -0
- package/package.json +1 -1
- package/src/boxes-and-lines/layout.ts +48 -8
- package/src/boxes-and-lines/parser.ts +59 -13
- package/src/boxes-and-lines/renderer.ts +33 -137
- package/src/c4/renderer.ts +25 -138
- package/src/class/renderer.ts +185 -186
- package/src/d3.ts +114 -191
- package/src/echarts.ts +99 -214
- package/src/er/renderer.ts +52 -245
- package/src/gantt/renderer.ts +140 -182
- package/src/index.ts +21 -1
- package/src/infra/renderer.ts +91 -244
- package/src/kanban/renderer.ts +22 -129
- package/src/org/renderer.ts +103 -170
- package/src/render.ts +39 -9
- package/src/sequence/renderer.ts +31 -151
- package/src/sitemap/layout.ts +180 -38
- package/src/sitemap/parser.ts +64 -23
- package/src/sitemap/renderer.ts +73 -161
- package/src/utils/legend-constants.ts +6 -0
- package/src/utils/legend-d3.ts +400 -0
- package/src/utils/legend-layout.ts +495 -0
- package/src/utils/legend-svg.ts +26 -0
- package/src/utils/legend-types.ts +169 -0
package/src/d3.ts
CHANGED
|
@@ -205,9 +205,14 @@ import {
|
|
|
205
205
|
LEGEND_ENTRY_FONT_SIZE as TL_LEGEND_ENTRY_FONT_SIZE,
|
|
206
206
|
LEGEND_ENTRY_DOT_GAP as TL_LEGEND_ENTRY_DOT_GAP,
|
|
207
207
|
LEGEND_ENTRY_TRAIL as TL_LEGEND_ENTRY_TRAIL,
|
|
208
|
-
LEGEND_GROUP_GAP as TL_LEGEND_GROUP_GAP,
|
|
209
208
|
measureLegendText,
|
|
210
209
|
} from './utils/legend-constants';
|
|
210
|
+
import { renderLegendD3 } from './utils/legend-d3';
|
|
211
|
+
import type {
|
|
212
|
+
LegendConfig,
|
|
213
|
+
LegendState,
|
|
214
|
+
LegendCallbacks,
|
|
215
|
+
} from './utils/legend-types';
|
|
211
216
|
import {
|
|
212
217
|
TITLE_FONT_SIZE,
|
|
213
218
|
TITLE_FONT_WEIGHT,
|
|
@@ -4866,7 +4871,7 @@ export function renderTimeline(
|
|
|
4866
4871
|
const LG_ENTRY_FONT_SIZE = TL_LEGEND_ENTRY_FONT_SIZE;
|
|
4867
4872
|
const LG_ENTRY_DOT_GAP = TL_LEGEND_ENTRY_DOT_GAP;
|
|
4868
4873
|
const LG_ENTRY_TRAIL = TL_LEGEND_ENTRY_TRAIL;
|
|
4869
|
-
|
|
4874
|
+
// LG_GROUP_GAP no longer needed — centralized legend handles spacing
|
|
4870
4875
|
const LG_ICON_W = 20; // swimlane icon area (icon + surrounding space) — local
|
|
4871
4876
|
|
|
4872
4877
|
const mainSvg = d3Selection.select(container).select<SVGSVGElement>('svg');
|
|
@@ -4875,10 +4880,6 @@ export function renderTimeline(
|
|
|
4875
4880
|
// Position legend at top, below title
|
|
4876
4881
|
const legendY = title ? 50 : 10;
|
|
4877
4882
|
|
|
4878
|
-
const groupBg = isDark
|
|
4879
|
-
? mix(palette.surface, palette.bg, 50)
|
|
4880
|
-
: mix(palette.surface, palette.bg, 30);
|
|
4881
|
-
|
|
4882
4883
|
// Pre-compute group widths (minified and expanded)
|
|
4883
4884
|
type LegendGroup = {
|
|
4884
4885
|
group: TagGroup;
|
|
@@ -4980,20 +4981,6 @@ export function renderTimeline(
|
|
|
4980
4981
|
|
|
4981
4982
|
if (visibleGroups.length === 0) return;
|
|
4982
4983
|
|
|
4983
|
-
// Compute total width and center horizontally in SVG
|
|
4984
|
-
const totalW =
|
|
4985
|
-
visibleGroups.reduce((s, lg) => {
|
|
4986
|
-
const isActive =
|
|
4987
|
-
viewMode ||
|
|
4988
|
-
(currentActiveGroup != null &&
|
|
4989
|
-
lg.group.name.toLowerCase() ===
|
|
4990
|
-
currentActiveGroup.toLowerCase());
|
|
4991
|
-
return s + (isActive ? lg.expandedWidth : lg.minifiedWidth);
|
|
4992
|
-
}, 0) +
|
|
4993
|
-
(visibleGroups.length - 1) * LG_GROUP_GAP;
|
|
4994
|
-
|
|
4995
|
-
let cx = (width - totalW) / 2;
|
|
4996
|
-
|
|
4997
4984
|
// Legend container for data-legend-active attribute
|
|
4998
4985
|
const legendContainer = mainSvg
|
|
4999
4986
|
.append('g')
|
|
@@ -5005,177 +4992,113 @@ export function renderTimeline(
|
|
|
5005
4992
|
);
|
|
5006
4993
|
}
|
|
5007
4994
|
|
|
5008
|
-
|
|
5009
|
-
|
|
5010
|
-
|
|
5011
|
-
|
|
5012
|
-
|
|
5013
|
-
|
|
5014
|
-
|
|
5015
|
-
|
|
5016
|
-
|
|
5017
|
-
|
|
5018
|
-
|
|
5019
|
-
|
|
5020
|
-
|
|
5021
|
-
|
|
5022
|
-
|
|
5023
|
-
|
|
5024
|
-
|
|
5025
|
-
|
|
5026
|
-
|
|
5027
|
-
|
|
5028
|
-
|
|
5029
|
-
|
|
5030
|
-
|
|
5031
|
-
|
|
5032
|
-
|
|
5033
|
-
currentActiveGroup
|
|
5034
|
-
|
|
5035
|
-
|
|
5036
|
-
|
|
5037
|
-
|
|
5038
|
-
|
|
5039
|
-
|
|
5040
|
-
|
|
5041
|
-
|
|
5042
|
-
|
|
5043
|
-
|
|
5044
|
-
|
|
5045
|
-
|
|
5046
|
-
|
|
5047
|
-
|
|
5048
|
-
|
|
5049
|
-
|
|
5050
|
-
|
|
5051
|
-
|
|
5052
|
-
|
|
5053
|
-
|
|
5054
|
-
|
|
5055
|
-
|
|
5056
|
-
|
|
5057
|
-
|
|
5058
|
-
|
|
5059
|
-
|
|
5060
|
-
|
|
5061
|
-
|
|
5062
|
-
|
|
5063
|
-
|
|
5064
|
-
|
|
5065
|
-
|
|
5066
|
-
|
|
5067
|
-
|
|
5068
|
-
|
|
5069
|
-
|
|
5070
|
-
|
|
5071
|
-
|
|
5072
|
-
|
|
5073
|
-
|
|
5074
|
-
|
|
5075
|
-
|
|
5076
|
-
|
|
5077
|
-
|
|
5078
|
-
|
|
5079
|
-
|
|
5080
|
-
|
|
5081
|
-
|
|
5082
|
-
|
|
5083
|
-
|
|
5084
|
-
|
|
5085
|
-
|
|
5086
|
-
|
|
5087
|
-
|
|
5088
|
-
|
|
5089
|
-
|
|
5090
|
-
|
|
5091
|
-
|
|
5092
|
-
|
|
5093
|
-
|
|
5094
|
-
|
|
5095
|
-
|
|
5096
|
-
|
|
5097
|
-
|
|
5098
|
-
|
|
5099
|
-
|
|
5100
|
-
|
|
5101
|
-
|
|
5102
|
-
|
|
5103
|
-
|
|
5104
|
-
|
|
5105
|
-
|
|
5106
|
-
|
|
5107
|
-
|
|
5108
|
-
|
|
5109
|
-
|
|
5110
|
-
|
|
5111
|
-
|
|
5112
|
-
|
|
5113
|
-
|
|
5114
|
-
|
|
5115
|
-
|
|
5116
|
-
const entryG = gEl
|
|
5117
|
-
.append('g')
|
|
5118
|
-
.attr('class', 'tl-tag-legend-entry')
|
|
5119
|
-
.attr('data-tag-group', tagKey)
|
|
5120
|
-
.attr('data-legend-entry', tagVal);
|
|
5121
|
-
|
|
5122
|
-
if (!viewMode) {
|
|
5123
|
-
entryG
|
|
5124
|
-
.style('cursor', 'pointer')
|
|
5125
|
-
.on('mouseenter', (event: MouseEvent) => {
|
|
5126
|
-
event.stopPropagation();
|
|
5127
|
-
fadeToTagValue(mainG, tagKey, tagVal);
|
|
5128
|
-
mainSvg
|
|
5129
|
-
.selectAll<SVGGElement, unknown>('.tl-tag-legend-entry')
|
|
5130
|
-
.each(function () {
|
|
5131
|
-
const el = d3Selection.select(this);
|
|
5132
|
-
const ev = el.attr('data-legend-entry');
|
|
5133
|
-
if (ev === '__group__') return;
|
|
5134
|
-
const eg = el.attr('data-tag-group');
|
|
5135
|
-
el.attr(
|
|
5136
|
-
'opacity',
|
|
5137
|
-
eg === tagKey && ev === tagVal ? 1 : FADE_OPACITY
|
|
5138
|
-
);
|
|
5139
|
-
});
|
|
5140
|
-
})
|
|
5141
|
-
.on('mouseleave', (event: MouseEvent) => {
|
|
5142
|
-
event.stopPropagation();
|
|
5143
|
-
fadeReset(mainG);
|
|
5144
|
-
mainSvg
|
|
5145
|
-
.selectAll<SVGGElement, unknown>('.tl-tag-legend-entry')
|
|
5146
|
-
.attr('opacity', 1);
|
|
5147
|
-
})
|
|
5148
|
-
.on('click', (event: MouseEvent) => {
|
|
5149
|
-
event.stopPropagation();
|
|
5150
|
-
});
|
|
5151
|
-
}
|
|
5152
|
-
|
|
5153
|
-
entryG
|
|
5154
|
-
.append('circle')
|
|
5155
|
-
.attr('cx', entryX + LG_DOT_R)
|
|
5156
|
-
.attr('cy', LG_HEIGHT / 2)
|
|
5157
|
-
.attr('r', LG_DOT_R)
|
|
5158
|
-
.attr('fill', entry.color);
|
|
5159
|
-
|
|
5160
|
-
const textX = entryX + LG_DOT_R * 2 + LG_ENTRY_DOT_GAP;
|
|
5161
|
-
entryG
|
|
5162
|
-
.append('text')
|
|
5163
|
-
.attr('x', textX)
|
|
5164
|
-
.attr('y', LG_HEIGHT / 2 + LG_ENTRY_FONT_SIZE / 2 - 1)
|
|
5165
|
-
.attr('font-size', LG_ENTRY_FONT_SIZE)
|
|
5166
|
-
.attr('font-family', FONT_FAMILY)
|
|
5167
|
-
.attr('fill', palette.textMuted)
|
|
5168
|
-
.text(entry.value);
|
|
5169
|
-
|
|
5170
|
-
entryX =
|
|
5171
|
-
textX +
|
|
5172
|
-
measureLegendText(entry.value, LG_ENTRY_FONT_SIZE) +
|
|
5173
|
-
LG_ENTRY_TRAIL;
|
|
5174
|
-
}
|
|
5175
|
-
}
|
|
5176
|
-
|
|
5177
|
-
cx += (isActive ? lg.expandedWidth : lg.minifiedWidth) + LG_GROUP_GAP;
|
|
5178
|
-
}
|
|
4995
|
+
// Render tag groups via centralized legend system
|
|
4996
|
+
const iconAddon = viewMode ? 0 : LG_ICON_W;
|
|
4997
|
+
const centralGroups = visibleGroups.map((lg) => ({
|
|
4998
|
+
name: lg.group.name,
|
|
4999
|
+
entries: lg.group.entries.map((e) => ({
|
|
5000
|
+
value: e.value,
|
|
5001
|
+
color: e.color,
|
|
5002
|
+
})),
|
|
5003
|
+
}));
|
|
5004
|
+
|
|
5005
|
+
// Determine effective active group for centralized renderer
|
|
5006
|
+
const centralActive = viewMode ? effectiveColorKey : currentActiveGroup;
|
|
5007
|
+
|
|
5008
|
+
const centralConfig: LegendConfig = {
|
|
5009
|
+
groups: centralGroups,
|
|
5010
|
+
position: { placement: 'top-center', titleRelation: 'below-title' },
|
|
5011
|
+
mode: 'fixed',
|
|
5012
|
+
capsulePillAddonWidth: iconAddon,
|
|
5013
|
+
};
|
|
5014
|
+
const centralState: LegendState = { activeGroup: centralActive };
|
|
5015
|
+
|
|
5016
|
+
const centralCallbacks: LegendCallbacks = viewMode
|
|
5017
|
+
? {}
|
|
5018
|
+
: {
|
|
5019
|
+
onGroupToggle: (groupName) => {
|
|
5020
|
+
currentActiveGroup =
|
|
5021
|
+
currentActiveGroup === groupName.toLowerCase()
|
|
5022
|
+
? null
|
|
5023
|
+
: groupName.toLowerCase();
|
|
5024
|
+
drawLegend();
|
|
5025
|
+
recolorEvents();
|
|
5026
|
+
onTagStateChange?.(currentActiveGroup, currentSwimlaneGroup);
|
|
5027
|
+
},
|
|
5028
|
+
onEntryHover: (groupName, entryValue) => {
|
|
5029
|
+
const tagKey = groupName.toLowerCase();
|
|
5030
|
+
if (entryValue) {
|
|
5031
|
+
const tagVal = entryValue.toLowerCase();
|
|
5032
|
+
fadeToTagValue(mainG, tagKey, tagVal);
|
|
5033
|
+
mainSvg
|
|
5034
|
+
.selectAll<SVGGElement, unknown>('[data-legend-entry]')
|
|
5035
|
+
.each(function () {
|
|
5036
|
+
const el = d3Selection.select(this);
|
|
5037
|
+
const ev = el.attr('data-legend-entry');
|
|
5038
|
+
const eg =
|
|
5039
|
+
el.attr('data-tag-group') ??
|
|
5040
|
+
(el.node() as Element)
|
|
5041
|
+
?.closest?.('[data-tag-group]')
|
|
5042
|
+
?.getAttribute('data-tag-group');
|
|
5043
|
+
el.attr(
|
|
5044
|
+
'opacity',
|
|
5045
|
+
eg === tagKey && ev === tagVal ? 1 : FADE_OPACITY
|
|
5046
|
+
);
|
|
5047
|
+
});
|
|
5048
|
+
} else {
|
|
5049
|
+
fadeReset(mainG);
|
|
5050
|
+
mainSvg
|
|
5051
|
+
.selectAll<SVGGElement, unknown>('[data-legend-entry]')
|
|
5052
|
+
.attr('opacity', 1);
|
|
5053
|
+
}
|
|
5054
|
+
},
|
|
5055
|
+
onGroupRendered: (groupName, groupEl, isActive) => {
|
|
5056
|
+
const groupKey = groupName.toLowerCase();
|
|
5057
|
+
groupEl.attr('data-tag-group', groupKey);
|
|
5058
|
+
if (isActive && !viewMode) {
|
|
5059
|
+
const isSwimActive =
|
|
5060
|
+
currentSwimlaneGroup != null &&
|
|
5061
|
+
currentSwimlaneGroup.toLowerCase() === groupKey;
|
|
5062
|
+
const pillWidth =
|
|
5063
|
+
measureLegendText(groupName, LG_PILL_FONT_SIZE) +
|
|
5064
|
+
LG_PILL_PAD;
|
|
5065
|
+
const pillXOff = LG_CAPSULE_PAD;
|
|
5066
|
+
const iconX = pillXOff + pillWidth + 5;
|
|
5067
|
+
const iconY = (LG_HEIGHT - 10) / 2;
|
|
5068
|
+
const iconEl = drawSwimlaneIcon(
|
|
5069
|
+
groupEl,
|
|
5070
|
+
iconX,
|
|
5071
|
+
iconY,
|
|
5072
|
+
isSwimActive
|
|
5073
|
+
);
|
|
5074
|
+
iconEl
|
|
5075
|
+
.attr('data-swimlane-toggle', groupKey)
|
|
5076
|
+
.on('click', (event: MouseEvent) => {
|
|
5077
|
+
event.stopPropagation();
|
|
5078
|
+
currentSwimlaneGroup =
|
|
5079
|
+
currentSwimlaneGroup === groupKey ? null : groupKey;
|
|
5080
|
+
onTagStateChange?.(
|
|
5081
|
+
currentActiveGroup,
|
|
5082
|
+
currentSwimlaneGroup
|
|
5083
|
+
);
|
|
5084
|
+
relayout();
|
|
5085
|
+
});
|
|
5086
|
+
}
|
|
5087
|
+
},
|
|
5088
|
+
};
|
|
5089
|
+
|
|
5090
|
+
const legendInnerG = legendContainer
|
|
5091
|
+
.append('g')
|
|
5092
|
+
.attr('transform', `translate(0, ${legendY})`);
|
|
5093
|
+
renderLegendD3(
|
|
5094
|
+
legendInnerG,
|
|
5095
|
+
centralConfig,
|
|
5096
|
+
centralState,
|
|
5097
|
+
palette,
|
|
5098
|
+
isDark,
|
|
5099
|
+
centralCallbacks,
|
|
5100
|
+
width
|
|
5101
|
+
);
|
|
5179
5102
|
}
|
|
5180
5103
|
|
|
5181
5104
|
// Build a quick lineNumber→event lookup
|