@diagrammo/dgmo 0.2.21 → 0.2.22
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/cli.cjs +90 -90
- package/dist/index.cjs +54 -40
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +54 -40
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/echarts.ts +7 -8
- package/src/kanban/renderer.ts +3 -1
- package/src/org/layout.ts +45 -25
- package/src/org/renderer.ts +19 -11
package/package.json
CHANGED
package/src/echarts.ts
CHANGED
|
@@ -1198,16 +1198,15 @@ function buildFunnelOption(
|
|
|
1198
1198
|
const val = p.value;
|
|
1199
1199
|
const prev = prevValueMap.get(p.name) ?? val;
|
|
1200
1200
|
const isFirst = p.dataIndex === 0;
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
if (!isFirst && topValue > 0) {
|
|
1201
|
+
if (isFirst) return '';
|
|
1202
|
+
const parts: string[] = [];
|
|
1203
|
+
const stepDrop = ((1 - val / prev) * 100).toFixed(1);
|
|
1204
|
+
parts.push(`Step drop-off: ${stepDrop}%`);
|
|
1205
|
+
if (topValue > 0) {
|
|
1207
1206
|
const totalDrop = ((1 - val / topValue) * 100).toFixed(1);
|
|
1208
|
-
|
|
1207
|
+
parts.push(`Overall drop-off: ${totalDrop}%`);
|
|
1209
1208
|
}
|
|
1210
|
-
return
|
|
1209
|
+
return parts.join('<br/>');
|
|
1211
1210
|
},
|
|
1212
1211
|
},
|
|
1213
1212
|
series: [
|
package/src/kanban/renderer.ts
CHANGED
|
@@ -267,7 +267,9 @@ export function renderKanban(
|
|
|
267
267
|
? parsed.title.length * TITLE_FONT_SIZE * 0.6 + 16
|
|
268
268
|
: 0;
|
|
269
269
|
let legendX = DIAGRAM_PADDING + titleTextWidth;
|
|
270
|
-
const groupBg =
|
|
270
|
+
const groupBg = isDark
|
|
271
|
+
? mix(palette.surface, palette.bg, 50)
|
|
272
|
+
: mix(palette.surface, palette.bg, 30);
|
|
271
273
|
const capsulePad = 4;
|
|
272
274
|
|
|
273
275
|
for (const group of parsed.tagGroups) {
|
package/src/org/layout.ts
CHANGED
|
@@ -52,10 +52,12 @@ export interface OrgContainerBounds {
|
|
|
52
52
|
export interface OrgLegendEntry {
|
|
53
53
|
value: string;
|
|
54
54
|
color: string;
|
|
55
|
+
isDefault?: boolean;
|
|
55
56
|
}
|
|
56
57
|
|
|
57
58
|
export interface OrgLegendGroup {
|
|
58
59
|
name: string;
|
|
60
|
+
alias?: string;
|
|
59
61
|
entries: OrgLegendEntry[];
|
|
60
62
|
x: number;
|
|
61
63
|
y: number;
|
|
@@ -276,15 +278,23 @@ function computeLegendGroups(tagGroups: OrgTagGroup[], _showEyeIcons: boolean):
|
|
|
276
278
|
for (const group of tagGroups) {
|
|
277
279
|
if (group.entries.length === 0) continue;
|
|
278
280
|
|
|
279
|
-
|
|
281
|
+
// Pill label includes alias if present (e.g., "Rank (r)")
|
|
282
|
+
const pillLabel = group.alias ? `${group.name} (${group.alias})` : group.name;
|
|
283
|
+
const pillWidth = pillLabel.length * LEGEND_PILL_FONT_W + LEGEND_PILL_PAD;
|
|
284
|
+
// Minified pill shows just the group name (no alias)
|
|
285
|
+
const minPillWidth = group.name.length * LEGEND_PILL_FONT_W + LEGEND_PILL_PAD;
|
|
280
286
|
|
|
281
287
|
// Capsule: pad + pill + gap + entries + pad
|
|
288
|
+
const isDefaultValue = group.defaultValue?.toLowerCase();
|
|
282
289
|
let entriesWidth = 0;
|
|
283
290
|
for (const entry of group.entries) {
|
|
291
|
+
const entryLabel = isDefaultValue === entry.value.toLowerCase()
|
|
292
|
+
? `${entry.value} (default)`
|
|
293
|
+
: entry.value;
|
|
284
294
|
entriesWidth +=
|
|
285
295
|
LEGEND_DOT_R * 2 +
|
|
286
296
|
LEGEND_ENTRY_DOT_GAP +
|
|
287
|
-
|
|
297
|
+
entryLabel.length * LEGEND_ENTRY_FONT_W +
|
|
288
298
|
LEGEND_ENTRY_TRAIL;
|
|
289
299
|
}
|
|
290
300
|
const capsuleWidth =
|
|
@@ -292,12 +302,17 @@ function computeLegendGroups(tagGroups: OrgTagGroup[], _showEyeIcons: boolean):
|
|
|
292
302
|
|
|
293
303
|
groups.push({
|
|
294
304
|
name: group.name,
|
|
295
|
-
|
|
305
|
+
alias: group.alias,
|
|
306
|
+
entries: group.entries.map((e) => ({
|
|
307
|
+
value: e.value,
|
|
308
|
+
color: e.color,
|
|
309
|
+
isDefault: group.defaultValue?.toLowerCase() === e.value.toLowerCase() || undefined,
|
|
310
|
+
})),
|
|
296
311
|
x: 0,
|
|
297
312
|
y: 0,
|
|
298
313
|
width: capsuleWidth,
|
|
299
314
|
height: LEGEND_HEIGHT,
|
|
300
|
-
minifiedWidth:
|
|
315
|
+
minifiedWidth: minPillWidth,
|
|
301
316
|
minifiedHeight: LEGEND_HEIGHT,
|
|
302
317
|
});
|
|
303
318
|
}
|
|
@@ -348,20 +363,22 @@ export function layoutOrg(
|
|
|
348
363
|
return { nodes: [], edges: [], containers: [], legend: [], width: 0, height: 0 };
|
|
349
364
|
}
|
|
350
365
|
|
|
351
|
-
//
|
|
352
|
-
let
|
|
366
|
+
// Legend-only mode: stack groups vertically, all expanded
|
|
367
|
+
let cy = MARGIN;
|
|
368
|
+
let maxWidth = 0;
|
|
353
369
|
for (const g of legendGroups) {
|
|
354
|
-
g.x =
|
|
355
|
-
g.y =
|
|
356
|
-
|
|
370
|
+
g.x = MARGIN;
|
|
371
|
+
g.y = cy;
|
|
372
|
+
cy += LEGEND_HEIGHT + LEGEND_GROUP_GAP;
|
|
373
|
+
if (g.width > maxWidth) maxWidth = g.width;
|
|
357
374
|
}
|
|
358
375
|
return {
|
|
359
376
|
nodes: [],
|
|
360
377
|
edges: [],
|
|
361
378
|
containers: [],
|
|
362
379
|
legend: legendGroups,
|
|
363
|
-
width:
|
|
364
|
-
height:
|
|
380
|
+
width: maxWidth + MARGIN * 2,
|
|
381
|
+
height: cy - LEGEND_GROUP_GAP + MARGIN,
|
|
365
382
|
};
|
|
366
383
|
}
|
|
367
384
|
|
|
@@ -1090,7 +1107,7 @@ export function layoutOrg(
|
|
|
1090
1107
|
let finalWidth = totalWidth;
|
|
1091
1108
|
let finalHeight = totalHeight;
|
|
1092
1109
|
|
|
1093
|
-
const legendPosition = parsed.options?.['legend-position'] ?? '
|
|
1110
|
+
const legendPosition = parsed.options?.['legend-position'] ?? 'top';
|
|
1094
1111
|
|
|
1095
1112
|
// When a tag group is active, only that group is laid out (full size).
|
|
1096
1113
|
// When none is active, all groups are laid out minified.
|
|
@@ -1133,28 +1150,31 @@ export function layoutOrg(
|
|
|
1133
1150
|
|
|
1134
1151
|
finalHeight = totalHeight + LEGEND_GAP + LEGEND_HEIGHT;
|
|
1135
1152
|
} else {
|
|
1136
|
-
// Top: horizontal row
|
|
1153
|
+
// Top: horizontal row above chart content, left-aligned
|
|
1154
|
+
const legendShift = LEGEND_HEIGHT + LEGEND_GROUP_GAP;
|
|
1155
|
+
|
|
1156
|
+
// Push all chart content down
|
|
1157
|
+
for (const n of layoutNodes) n.y += legendShift;
|
|
1158
|
+
for (const c of containers) c.y += legendShift;
|
|
1159
|
+
for (const e of layoutEdges) {
|
|
1160
|
+
for (const p of e.points) p.y += legendShift;
|
|
1161
|
+
}
|
|
1162
|
+
|
|
1137
1163
|
const totalGroupsWidth =
|
|
1138
1164
|
visibleGroups.reduce((s, g) => s + effectiveW(g), 0) +
|
|
1139
1165
|
(visibleGroups.length - 1) * LEGEND_GROUP_GAP;
|
|
1140
|
-
const legendStartX = totalWidth - MARGIN + LEGEND_GAP;
|
|
1141
|
-
const legendY = MARGIN;
|
|
1142
1166
|
|
|
1143
|
-
let cx =
|
|
1167
|
+
let cx = MARGIN;
|
|
1144
1168
|
for (const g of visibleGroups) {
|
|
1145
1169
|
g.x = cx;
|
|
1146
|
-
g.y =
|
|
1170
|
+
g.y = MARGIN;
|
|
1147
1171
|
cx += effectiveW(g) + LEGEND_GROUP_GAP;
|
|
1148
1172
|
}
|
|
1149
1173
|
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
const legendBottom = legendY + LEGEND_HEIGHT + MARGIN;
|
|
1156
|
-
if (legendBottom > finalHeight) {
|
|
1157
|
-
finalHeight = legendBottom;
|
|
1174
|
+
finalHeight += legendShift;
|
|
1175
|
+
const neededWidth = totalGroupsWidth + MARGIN * 2;
|
|
1176
|
+
if (neededWidth > finalWidth) {
|
|
1177
|
+
finalWidth = neededWidth;
|
|
1158
1178
|
}
|
|
1159
1179
|
}
|
|
1160
1180
|
}
|
package/src/org/renderer.ts
CHANGED
|
@@ -447,27 +447,34 @@ export function renderOrg(
|
|
|
447
447
|
|
|
448
448
|
// Render legend — kanban-style pills.
|
|
449
449
|
// Skip in export mode (unless legend-only chart).
|
|
450
|
+
// Legend-only (no nodes): all groups rendered as expanded capsules.
|
|
450
451
|
// Active group: only that group rendered as capsule (pill + entries).
|
|
451
452
|
// No active group: all groups rendered as standalone pills.
|
|
452
|
-
|
|
453
|
+
const legendOnly = layout.nodes.length === 0;
|
|
454
|
+
if (!exportDims || legendOnly) for (const group of layout.legend) {
|
|
453
455
|
const isActive =
|
|
454
|
-
|
|
455
|
-
|
|
456
|
+
legendOnly ||
|
|
457
|
+
(activeTagGroup != null &&
|
|
458
|
+
group.name.toLowerCase() === activeTagGroup.toLowerCase());
|
|
456
459
|
|
|
457
|
-
// When a group is active, skip all other groups entirely
|
|
458
|
-
if (activeTagGroup != null && !isActive) continue;
|
|
460
|
+
// When a group is active, skip all other groups entirely (not in legend-only mode)
|
|
461
|
+
if (!legendOnly && activeTagGroup != null && !isActive) continue;
|
|
459
462
|
|
|
460
|
-
const groupBg =
|
|
463
|
+
const groupBg = isDark
|
|
464
|
+
? mix(palette.surface, palette.bg, 50)
|
|
465
|
+
: mix(palette.surface, palette.bg, 30);
|
|
461
466
|
|
|
467
|
+
// Pill label: include alias when expanded (e.g., "Rank (r)")
|
|
468
|
+
const pillLabel = isActive && group.alias ? `${group.name} (${group.alias})` : group.name;
|
|
462
469
|
const pillWidth =
|
|
463
|
-
|
|
470
|
+
pillLabel.length * LEGEND_PILL_FONT_W + LEGEND_PILL_PAD;
|
|
464
471
|
|
|
465
472
|
const gEl = contentG
|
|
466
473
|
.append('g')
|
|
467
474
|
.attr('transform', `translate(${group.x}, ${group.y})`)
|
|
468
475
|
.attr('class', 'org-legend-group')
|
|
469
476
|
.attr('data-legend-group', group.name.toLowerCase())
|
|
470
|
-
.style('cursor', 'pointer');
|
|
477
|
+
.style('cursor', legendOnly ? 'default' : 'pointer');
|
|
471
478
|
|
|
472
479
|
// Outer capsule background (active only)
|
|
473
480
|
if (isActive) {
|
|
@@ -516,7 +523,7 @@ export function renderOrg(
|
|
|
516
523
|
.attr('font-weight', '500')
|
|
517
524
|
.attr('fill', isActive ? palette.text : palette.textMuted)
|
|
518
525
|
.attr('text-anchor', 'middle')
|
|
519
|
-
.text(
|
|
526
|
+
.text(pillLabel);
|
|
520
527
|
|
|
521
528
|
// Entries inside capsule (active only)
|
|
522
529
|
if (isActive) {
|
|
@@ -530,15 +537,16 @@ export function renderOrg(
|
|
|
530
537
|
.attr('fill', entry.color);
|
|
531
538
|
|
|
532
539
|
const textX = entryX + LEGEND_DOT_R * 2 + LEGEND_ENTRY_DOT_GAP;
|
|
540
|
+
const entryLabel = entry.isDefault ? `${entry.value} (default)` : entry.value;
|
|
533
541
|
gEl
|
|
534
542
|
.append('text')
|
|
535
543
|
.attr('x', textX)
|
|
536
544
|
.attr('y', LEGEND_HEIGHT / 2 + LEGEND_ENTRY_FONT_SIZE / 2 - 1)
|
|
537
545
|
.attr('font-size', LEGEND_ENTRY_FONT_SIZE)
|
|
538
546
|
.attr('fill', palette.textMuted)
|
|
539
|
-
.text(
|
|
547
|
+
.text(entryLabel);
|
|
540
548
|
|
|
541
|
-
entryX = textX +
|
|
549
|
+
entryX = textX + entryLabel.length * LEGEND_ENTRY_FONT_W + LEGEND_ENTRY_TRAIL;
|
|
542
550
|
}
|
|
543
551
|
}
|
|
544
552
|
}
|