@diagrammo/dgmo 0.87.0 → 0.88.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/advanced.d.ts +6 -0
- package/dist/advanced.js +8 -6
- package/dist/block.js +3 -2
- package/dist/{chunk-FBKPSU3A.js → chunk-5LD7SHXO.js} +1 -1
- package/dist/{chunk-Q2U5635V.js → chunk-ABP5BUD6.js} +9 -430
- package/dist/{chunk-LPXC4I4R.js → chunk-JLETTWUJ.js} +12 -9
- package/dist/{chunk-AGO3PFGS.js → chunk-MVCPO6LI.js} +15 -10
- package/dist/{chunk-PJQZWJ5I.js → chunk-VW52W5F6.js} +934 -82
- package/dist/chunk-WXPBP4IT.js +438 -0
- package/dist/index.js +3 -2
- package/dist/{renderer-NX4YKWCJ.js → renderer-RNCYODNF.js} +1 -1
- package/dist/{renderer-3CTUWCO7.js → renderer-TPOZOAXR.js} +2 -2
- package/dist/{renderer-ELYPDPNF.js → renderer-XUPLAO4B.js} +2 -1
- package/package.json +1 -1
package/dist/advanced.d.ts
CHANGED
|
@@ -3609,6 +3609,12 @@ interface GanttInteractiveOptions {
|
|
|
3609
3609
|
* change), matching every other ControlsStrip-hosted chart type.
|
|
3610
3610
|
*/
|
|
3611
3611
|
controlsHost?: 'app' | 'inline';
|
|
3612
|
+
/**
|
|
3613
|
+
* The host scales the SVG to fit the container on both axes (the app's
|
|
3614
|
+
* preview pane). A chart taller than the container is laid out wider, to the
|
|
3615
|
+
* container's aspect ratio, so it fills the width instead of a centred column.
|
|
3616
|
+
*/
|
|
3617
|
+
fitToContainer?: boolean;
|
|
3612
3618
|
/** Initial Critical Path highlight state (used when `controlsHost` is `'app'`). */
|
|
3613
3619
|
criticalPathActive?: boolean;
|
|
3614
3620
|
/** Initial Dependencies-arrow visibility (used when `controlsHost` is `'app'`). */
|
package/dist/advanced.js
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
|
-
layoutBracket,
|
|
3
2
|
renderBracket,
|
|
4
3
|
renderBracketForExport
|
|
5
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-WXPBP4IT.js";
|
|
6
5
|
import {
|
|
7
6
|
renderRaci,
|
|
8
7
|
renderRaciForExport
|
|
@@ -134,7 +133,7 @@ import {
|
|
|
134
133
|
import {
|
|
135
134
|
buildTagLaneRowList,
|
|
136
135
|
renderGantt
|
|
137
|
-
} from "./chunk-
|
|
136
|
+
} from "./chunk-MVCPO6LI.js";
|
|
138
137
|
import {
|
|
139
138
|
renderState,
|
|
140
139
|
renderStateForExport
|
|
@@ -295,7 +294,7 @@ import {
|
|
|
295
294
|
focusEventLine,
|
|
296
295
|
renderEventLine,
|
|
297
296
|
renderEventLineForExport
|
|
298
|
-
} from "./chunk-
|
|
297
|
+
} from "./chunk-5LD7SHXO.js";
|
|
299
298
|
import "./chunk-NPWVOFB3.js";
|
|
300
299
|
import "./chunk-R2K5L4UA.js";
|
|
301
300
|
import {
|
|
@@ -323,7 +322,10 @@ import {
|
|
|
323
322
|
renderSlopeChart,
|
|
324
323
|
renderVenn,
|
|
325
324
|
renderWordCloud
|
|
326
|
-
} from "./chunk-
|
|
325
|
+
} from "./chunk-VW52W5F6.js";
|
|
326
|
+
import {
|
|
327
|
+
layoutBracket
|
|
328
|
+
} from "./chunk-ABP5BUD6.js";
|
|
327
329
|
import "./chunk-D4M25W6R.js";
|
|
328
330
|
import {
|
|
329
331
|
CHART_TYPE_DESCRIPTIONS,
|
|
@@ -377,7 +379,7 @@ import "./chunk-2CW5ZTNH.js";
|
|
|
377
379
|
import {
|
|
378
380
|
formatDateLabel,
|
|
379
381
|
renderTimeline
|
|
380
|
-
} from "./chunk-
|
|
382
|
+
} from "./chunk-JLETTWUJ.js";
|
|
381
383
|
import {
|
|
382
384
|
computeTimeTicks
|
|
383
385
|
} from "./chunk-YRYKTYMQ.js";
|
package/dist/block.js
CHANGED
|
@@ -4,7 +4,8 @@ import {
|
|
|
4
4
|
} from "./chunk-D3K4U3ZM.js";
|
|
5
5
|
import {
|
|
6
6
|
render
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-VW52W5F6.js";
|
|
8
|
+
import "./chunk-ABP5BUD6.js";
|
|
8
9
|
import {
|
|
9
10
|
docsLink,
|
|
10
11
|
renderErrorCard
|
|
@@ -23,7 +24,7 @@ import "./chunk-2CW5ZTNH.js";
|
|
|
23
24
|
import {
|
|
24
25
|
escapeAttr,
|
|
25
26
|
escapeHtml
|
|
26
|
-
} from "./chunk-
|
|
27
|
+
} from "./chunk-JLETTWUJ.js";
|
|
27
28
|
import "./chunk-YRYKTYMQ.js";
|
|
28
29
|
import "./chunk-24YPR5EH.js";
|
|
29
30
|
import "./chunk-XCKNFFKM.js";
|
|
@@ -1,51 +1,10 @@
|
|
|
1
|
-
import {
|
|
2
|
-
renderInlineText
|
|
3
|
-
} from "./chunk-S7W75MIM.js";
|
|
4
|
-
import {
|
|
5
|
-
layoutInlineHeader
|
|
6
|
-
} from "./chunk-3DOQOXXI.js";
|
|
7
|
-
import {
|
|
8
|
-
TITLE_FONT_SIZE,
|
|
9
|
-
TITLE_FONT_WEIGHT,
|
|
10
|
-
TITLE_Y
|
|
11
|
-
} from "./chunk-VLC6WNRD.js";
|
|
12
|
-
import {
|
|
13
|
-
renderIntegratedLegend
|
|
14
|
-
} from "./chunk-LHL6HDUL.js";
|
|
15
|
-
import {
|
|
16
|
-
getLegendExtent,
|
|
17
|
-
getMaxLegendReservedHeight
|
|
18
|
-
} from "./chunk-FM3WAVB6.js";
|
|
19
|
-
import {
|
|
20
|
-
FONT_FAMILY
|
|
21
|
-
} from "./chunk-ICLB27PZ.js";
|
|
22
|
-
import {
|
|
23
|
-
LEGEND_GROUP_GAP
|
|
24
|
-
} from "./chunk-RH2PROYG.js";
|
|
25
1
|
import {
|
|
26
2
|
measureText
|
|
27
3
|
} from "./chunk-KAO7JL4I.js";
|
|
28
|
-
import {
|
|
29
|
-
resolveTagColor,
|
|
30
|
-
tagAttrKey
|
|
31
|
-
} from "./chunk-L5AFONQU.js";
|
|
32
|
-
import {
|
|
33
|
-
contrastText,
|
|
34
|
-
getSeriesColors,
|
|
35
|
-
mix,
|
|
36
|
-
shapeFill,
|
|
37
|
-
themeBaseBg
|
|
38
|
-
} from "./chunk-7HWY4QAJ.js";
|
|
39
|
-
import {
|
|
40
|
-
resolveColor
|
|
41
|
-
} from "./chunk-2M4IGSCY.js";
|
|
42
4
|
import {
|
|
43
5
|
makeDgmoError
|
|
44
6
|
} from "./chunk-BYU54C3S.js";
|
|
45
7
|
|
|
46
|
-
// src/bracket/renderer.ts
|
|
47
|
-
import * as d3Selection from "d3-selection";
|
|
48
|
-
|
|
49
8
|
// src/bracket/layout.ts
|
|
50
9
|
var BOX_W = 176;
|
|
51
10
|
var BOX_H = 30;
|
|
@@ -390,8 +349,8 @@ function layoutBracket(parsed) {
|
|
|
390
349
|
(g) => g.built.nodes.map((n) => commentaryLineCount(n.commentary))
|
|
391
350
|
)
|
|
392
351
|
);
|
|
393
|
-
const
|
|
394
|
-
const rowScale = maxComment > 0 ? Math.max(ROW, 2 * BOX_H + PAIR_GAP + maxComment *
|
|
352
|
+
const COMMENT_LH = 15;
|
|
353
|
+
const rowScale = maxComment > 0 ? Math.max(ROW, 2 * BOX_H + PAIR_GAP + maxComment * COMMENT_LH + 16) : ROW;
|
|
395
354
|
for (const g of groups) positionSide(g.built, rowScale);
|
|
396
355
|
const champResults = parsed.matches.filter(
|
|
397
356
|
(m) => m.side === null && parsed.sides.length >= 1
|
|
@@ -560,7 +519,7 @@ function layoutBracket(parsed) {
|
|
|
560
519
|
const wl = commentaryLineCount(m.commentary);
|
|
561
520
|
maxY = Math.max(
|
|
562
521
|
maxY,
|
|
563
|
-
m.y + half + BOX_H / 2 + 13 + (wl - 1) *
|
|
522
|
+
m.y + half + BOX_H / 2 + 13 + (wl - 1) * COMMENT_LH + 4
|
|
564
523
|
);
|
|
565
524
|
}
|
|
566
525
|
}
|
|
@@ -613,391 +572,11 @@ function layoutBracket(parsed) {
|
|
|
613
572
|
};
|
|
614
573
|
}
|
|
615
574
|
|
|
616
|
-
// src/bracket/renderer.ts
|
|
617
|
-
var PADDING = 16;
|
|
618
|
-
var TITLE_BAND = 40;
|
|
619
|
-
var HALF_SPAN2 = (BOX_H + PAIR_GAP) / 2;
|
|
620
|
-
var COMMENT_LH = 15;
|
|
621
|
-
var LEGEND_POSITION = {
|
|
622
|
-
placement: "top-center",
|
|
623
|
-
titleRelation: "below-title"
|
|
624
|
-
};
|
|
625
|
-
function clip(s, maxW, fs, bold) {
|
|
626
|
-
if (measureText(s, fs, { bold }) <= maxW) return s;
|
|
627
|
-
let lo = 0;
|
|
628
|
-
let hi = s.length;
|
|
629
|
-
while (lo < hi) {
|
|
630
|
-
const mid = Math.ceil((lo + hi) / 2);
|
|
631
|
-
if (measureText(s.slice(0, mid) + "\u2026", fs, { bold }) <= maxW) lo = mid;
|
|
632
|
-
else hi = mid - 1;
|
|
633
|
-
}
|
|
634
|
-
return lo <= 0 ? "" : s.slice(0, lo) + "\u2026";
|
|
635
|
-
}
|
|
636
|
-
function paintForAccent(palette, isDark, accent, fillMode) {
|
|
637
|
-
const base = themeBaseBg(palette, isDark);
|
|
638
|
-
return {
|
|
639
|
-
accent,
|
|
640
|
-
ink: palette.text,
|
|
641
|
-
muted: mix(palette.text, base, 52),
|
|
642
|
-
// Loser / TBD outlines + connectors are ink-based (not the pale border) so
|
|
643
|
-
// they stay legible on top of a colored, shaded column band.
|
|
644
|
-
hair: mix(palette.text, base, 34),
|
|
645
|
-
wire: mix(palette.text, base, 30),
|
|
646
|
-
track: mix(palette.border, base, 55),
|
|
647
|
-
// §1.9 fill family rides through shapeFill: solid ⇒ raw accent,
|
|
648
|
-
// outline ⇒ theme base bg (accent stays on the stroke), absent ⇒ 25% tint.
|
|
649
|
-
tint: shapeFill(palette, accent, isDark, { mode: fillMode })
|
|
650
|
-
};
|
|
651
|
-
}
|
|
652
|
-
function paintFor(palette, isDark, sideColor, fillMode) {
|
|
653
|
-
const accent = sideColor && resolveColor(sideColor, palette) || sideColor || getSeriesColors(palette)[0];
|
|
654
|
-
return paintForAccent(palette, isDark, accent, fillMode);
|
|
655
|
-
}
|
|
656
|
-
function resolveBracketLegend(parsed, palette) {
|
|
657
|
-
const activeTag = parsed.activeTag;
|
|
658
|
-
const activeGroup = activeTag ? parsed.tagGroups.find((g) => tagAttrKey(g.name) === tagAttrKey(activeTag)) : void 0;
|
|
659
|
-
const legendGroups = activeGroup && !parsed.noLegend ? [
|
|
660
|
-
{
|
|
661
|
-
name: activeGroup.name,
|
|
662
|
-
entries: activeGroup.entries.map((e) => ({
|
|
663
|
-
value: e.value,
|
|
664
|
-
color: resolveColor(e.color, palette) ?? e.color
|
|
665
|
-
}))
|
|
666
|
-
}
|
|
667
|
-
] : [];
|
|
668
|
-
return { activeGroup, legendGroups };
|
|
669
|
-
}
|
|
670
|
-
function bracketExportDimensions(parsed, palette) {
|
|
671
|
-
const layout = layoutBracket(parsed);
|
|
672
|
-
const width = layout.width + PADDING * 2;
|
|
673
|
-
const titleH = parsed.title ? TITLE_BAND : 0;
|
|
674
|
-
const { legendGroups } = resolveBracketLegend(parsed, palette);
|
|
675
|
-
const legendReserve = legendGroups.length > 0 ? getMaxLegendReservedHeight(
|
|
676
|
-
{ groups: legendGroups, position: LEGEND_POSITION, mode: "export" },
|
|
677
|
-
width
|
|
678
|
-
) + LEGEND_GROUP_GAP : 0;
|
|
679
|
-
const height = titleH + legendReserve + PADDING + layout.height + PADDING;
|
|
680
|
-
return { width, height };
|
|
681
|
-
}
|
|
682
|
-
function renderBracket(container, parsed, palette, isDark, exportDims) {
|
|
683
|
-
d3Selection.select(container).selectAll(":not([data-d3-tooltip])").remove();
|
|
684
|
-
if (parsed.error) return;
|
|
685
|
-
const layout = layoutBracket(parsed);
|
|
686
|
-
const width = exportDims?.width ?? container.clientWidth;
|
|
687
|
-
const height = exportDims?.height ?? container.clientHeight;
|
|
688
|
-
if (width <= 0 || height <= 0) return;
|
|
689
|
-
const showTitle = !!parsed.title;
|
|
690
|
-
const titleH = showTitle ? TITLE_BAND : 0;
|
|
691
|
-
const exportMode = exportDims !== void 0;
|
|
692
|
-
const seedByName = /* @__PURE__ */ new Map();
|
|
693
|
-
for (const s of parsed.seeds)
|
|
694
|
-
if (!seedByName.has(s.name)) seedByName.set(s.name, s.seed);
|
|
695
|
-
const activeTag = parsed.activeTag;
|
|
696
|
-
const activeKey = activeTag ? tagAttrKey(activeTag) : null;
|
|
697
|
-
const tagValueOf = (name) => {
|
|
698
|
-
if (!name || !activeKey) return null;
|
|
699
|
-
const meta = parsed.competitorMeta.get(name);
|
|
700
|
-
return meta?.[activeKey] ?? null;
|
|
701
|
-
};
|
|
702
|
-
const tagColorOf = (name) => {
|
|
703
|
-
if (!name || !activeTag) return null;
|
|
704
|
-
const meta = parsed.competitorMeta.get(name);
|
|
705
|
-
if (!meta || !activeKey || !meta[activeKey]) return null;
|
|
706
|
-
const c = resolveTagColor(meta, [...parsed.tagGroups], activeTag);
|
|
707
|
-
return c ? resolveColor(c, palette) ?? c : null;
|
|
708
|
-
};
|
|
709
|
-
const { activeGroup, legendGroups } = resolveBracketLegend(parsed, palette);
|
|
710
|
-
const legendReserve = legendGroups.length > 0 ? getMaxLegendReservedHeight(
|
|
711
|
-
{ groups: legendGroups, position: LEGEND_POSITION, mode: "preview" },
|
|
712
|
-
width
|
|
713
|
-
) + LEGEND_GROUP_GAP : 0;
|
|
714
|
-
const inlineRequested = parsed.legendInline === true;
|
|
715
|
-
const legendExtent = inlineRequested && legendGroups.length > 0 ? getLegendExtent(
|
|
716
|
-
{
|
|
717
|
-
groups: legendGroups,
|
|
718
|
-
position: {
|
|
719
|
-
placement: "top-center",
|
|
720
|
-
titleRelation: "inline-with-title"
|
|
721
|
-
},
|
|
722
|
-
mode: exportMode ? "export" : "preview"
|
|
723
|
-
},
|
|
724
|
-
{ activeGroup: activeGroup?.name ?? null },
|
|
725
|
-
width
|
|
726
|
-
) : { width: 0, height: 0 };
|
|
727
|
-
const header = layoutInlineHeader({
|
|
728
|
-
requested: inlineRequested,
|
|
729
|
-
title: parsed.title ?? "",
|
|
730
|
-
hasLegend: legendGroups.length > 0,
|
|
731
|
-
legendWidth: legendExtent.width,
|
|
732
|
-
legendHeight: legendExtent.height,
|
|
733
|
-
containerWidth: width,
|
|
734
|
-
titleBandHeight: titleH,
|
|
735
|
-
legendReserve,
|
|
736
|
-
titleBaselineY: TITLE_Y,
|
|
737
|
-
titleFontSize: TITLE_FONT_SIZE
|
|
738
|
-
});
|
|
739
|
-
const svg = d3Selection.select(container).append("svg").attr("width", width).attr("height", height).attr("viewBox", `0 0 ${width} ${height}`).attr("preserveAspectRatio", "xMidYMin meet").attr("xmlns", "http://www.w3.org/2000/svg").attr("class", "dgmo-bracket").attr("role", "img").attr(
|
|
740
|
-
"aria-label",
|
|
741
|
-
`Tournament bracket${parsed.title ? ` \u2014 ${parsed.title}` : ""}${layout.champion ? `, champion ${layout.champion}` : ""}`
|
|
742
|
-
).style("font-family", FONT_FAMILY);
|
|
743
|
-
if (showTitle) {
|
|
744
|
-
const t = svg.append("text").attr("class", "chart-title").attr("x", header.titleX).attr("y", TITLE_Y).attr("text-anchor", header.titleAnchor).attr("fill", palette.text).attr("font-family", FONT_FAMILY).attr("font-size", TITLE_FONT_SIZE).attr("font-weight", TITLE_FONT_WEIGHT).text(parsed.title);
|
|
745
|
-
if (parsed.titleLineNumber !== null)
|
|
746
|
-
t.attr("data-line-number", parsed.titleLineNumber);
|
|
747
|
-
}
|
|
748
|
-
const areaY = (header.inline ? titleH : titleH + legendReserve) + PADDING;
|
|
749
|
-
const areaX = PADDING;
|
|
750
|
-
const areaW = Math.max(1, width - PADDING * 2);
|
|
751
|
-
const areaH = Math.max(1, height - areaY - PADDING);
|
|
752
|
-
const scale = Math.min(1, areaW / layout.width, areaH / layout.height);
|
|
753
|
-
const offsetX = areaX + (areaW - layout.width * scale) / 2;
|
|
754
|
-
const offsetY = areaY;
|
|
755
|
-
const root = svg.append("g").attr("class", "dgmo-bracket-body").attr("transform", `translate(${offsetX},${offsetY}) scale(${scale})`);
|
|
756
|
-
if (legendGroups.length > 0) {
|
|
757
|
-
const legendG = svg.append("g").attr("class", "dgmo-bracket-legend").attr(
|
|
758
|
-
"transform",
|
|
759
|
-
header.inline ? `translate(${header.legendX}, ${header.legendY})` : `translate(0, ${titleH})`
|
|
760
|
-
);
|
|
761
|
-
renderIntegratedLegend(legendG, {
|
|
762
|
-
groups: legendGroups,
|
|
763
|
-
palette: {
|
|
764
|
-
bg: palette.bg,
|
|
765
|
-
surface: palette.surface,
|
|
766
|
-
text: palette.text,
|
|
767
|
-
textMuted: palette.textMuted,
|
|
768
|
-
primary: palette.primary
|
|
769
|
-
},
|
|
770
|
-
isDark,
|
|
771
|
-
width,
|
|
772
|
-
mode: exportMode ? "export" : "preview",
|
|
773
|
-
position: {
|
|
774
|
-
placement: "top-center",
|
|
775
|
-
titleRelation: header.inline ? "inline-with-title" : "below-title"
|
|
776
|
-
},
|
|
777
|
-
activeGroup: activeGroup?.name ?? null
|
|
778
|
-
});
|
|
779
|
-
}
|
|
780
|
-
const baseAccent = parsed.accentColor ?? resolveColor("blue", palette) ?? getSeriesColors(palette)[0];
|
|
781
|
-
const fillMode = parsed.fillMode;
|
|
782
|
-
const basePaint = paintForAccent(palette, isDark, baseAccent, fillMode);
|
|
783
|
-
const sideColorByLabel = /* @__PURE__ */ new Map();
|
|
784
|
-
for (const s of parsed.sides) sideColorByLabel.set(s.label, s.color);
|
|
785
|
-
const sideBarH = layout.sideLabels.length ? 30 : 0;
|
|
786
|
-
const bandTop = sideBarH + 2;
|
|
787
|
-
const roundY = sideBarH + 24;
|
|
788
|
-
for (const col of layout.columns) {
|
|
789
|
-
if (!col.color) continue;
|
|
790
|
-
const covered = layout.sideLabels.some(
|
|
791
|
-
(s) => col.x >= s.x0 && col.x <= s.x1
|
|
792
|
-
);
|
|
793
|
-
const top = covered ? bandTop : 2;
|
|
794
|
-
const c = resolveColor(col.color, palette) ?? col.color;
|
|
795
|
-
root.append("rect").attr("x", col.x - BOX_W / 2 - 10).attr("y", top).attr("width", BOX_W + 20).attr("height", Math.max(0, layout.height - top)).attr("rx", 10).attr(
|
|
796
|
-
"fill",
|
|
797
|
-
fillMode === "outline" ? themeBaseBg(palette, isDark) : mix(c, palette.bg, isDark ? 12 : 8)
|
|
798
|
-
).call((sel) => {
|
|
799
|
-
if (fillMode === "outline")
|
|
800
|
-
sel.attr("stroke", mix(c, palette.bg, 35)).attr("stroke-width", 1);
|
|
801
|
-
});
|
|
802
|
-
}
|
|
803
|
-
for (const s of layout.sideLabels) {
|
|
804
|
-
const c = s.color ? resolveColor(s.color, palette) ?? s.color : basePaint.accent;
|
|
805
|
-
root.append("rect").attr("x", s.x0 - 8).attr("y", 2).attr("width", s.x1 - s.x0 + 16).attr("height", 24).attr("rx", 8).attr(
|
|
806
|
-
"fill",
|
|
807
|
-
fillMode === "outline" ? themeBaseBg(palette, isDark) : mix(c, palette.bg, isDark ? 20 : 14)
|
|
808
|
-
).attr("stroke", fillMode === "outline" ? c : mix(c, palette.bg, 55)).attr("stroke-width", 1);
|
|
809
|
-
root.append("text").attr("x", (s.x0 + s.x1) / 2).attr("y", 18).attr("text-anchor", "middle").attr("font-size", 13).attr("font-weight", "bold").attr("letter-spacing", 0.3).attr("fill", c).text(clip(s.label, s.x1 - s.x0, 13, true));
|
|
810
|
-
}
|
|
811
|
-
for (const col of layout.columns) {
|
|
812
|
-
const c = col.color ? resolveColor(col.color, palette) ?? col.color : basePaint.muted;
|
|
813
|
-
const covered = layout.sideLabels.some(
|
|
814
|
-
(s) => col.x >= s.x0 && col.x <= s.x1
|
|
815
|
-
);
|
|
816
|
-
root.append("text").attr("x", col.x).attr("y", covered ? roundY : 22).attr("text-anchor", "middle").attr("font-size", covered ? 14 : 15).attr("font-weight", "bold").attr("fill", c).attr("letter-spacing", 0.4).text(clip(col.label, BOX_W + 20, covered ? 14 : 15, true));
|
|
817
|
-
}
|
|
818
|
-
for (const m of layout.matches) {
|
|
819
|
-
drawConnector(root, m, m.topFrom, m.y - HALF_SPAN2, basePaint);
|
|
820
|
-
drawConnector(root, m, m.botFrom, m.y + HALF_SPAN2, basePaint);
|
|
821
|
-
}
|
|
822
|
-
for (const m of layout.matches) {
|
|
823
|
-
const sidePaint = m.side && sideColorByLabel.get(m.side) ? paintFor(palette, isDark, sideColorByLabel.get(m.side), fillMode) : basePaint;
|
|
824
|
-
const topSeed = seedByName.get(m.top ?? "") ?? null;
|
|
825
|
-
const botSeed = seedByName.get(m.bot ?? "") ?? null;
|
|
826
|
-
let homeName = m.home;
|
|
827
|
-
if (!homeName && topSeed !== null && botSeed !== null) {
|
|
828
|
-
homeName = topSeed <= botSeed ? m.top : m.bot;
|
|
829
|
-
}
|
|
830
|
-
const [wSeed, lSeed] = m.winner === "top" ? [topSeed, botSeed] : m.winner === "bot" ? [botSeed, topSeed] : [null, null];
|
|
831
|
-
const isUpset = wSeed !== null && lSeed !== null && wSeed > lSeed && !m.isChampionship;
|
|
832
|
-
const scoreParts = m.score ? m.score.split(/[-–]/).map((p) => p.trim()) : null;
|
|
833
|
-
const winScore = scoreParts?.length === 2 ? scoreParts[0] : "";
|
|
834
|
-
const loseScore = scoreParts?.length === 2 ? scoreParts[1] : "";
|
|
835
|
-
const champWinner = m.isChampionship ? layout.champion : null;
|
|
836
|
-
const mkInfo = (side, name) => {
|
|
837
|
-
const isWinner = m.winner === side;
|
|
838
|
-
const isLoser = m.winner !== null && m.winner !== side;
|
|
839
|
-
return {
|
|
840
|
-
isWinner,
|
|
841
|
-
isLoser,
|
|
842
|
-
pending: m.pending,
|
|
843
|
-
isChampion: champWinner !== null && isWinner,
|
|
844
|
-
score: isWinner ? winScore : isLoser ? loseScore : "",
|
|
845
|
-
seed: seedByName.get(name ?? "") ?? null,
|
|
846
|
-
tagColor: tagColorOf(name),
|
|
847
|
-
tagValue: tagValueOf(name),
|
|
848
|
-
isHome: homeName !== null && homeName === name
|
|
849
|
-
};
|
|
850
|
-
};
|
|
851
|
-
drawBox(
|
|
852
|
-
root,
|
|
853
|
-
m.x,
|
|
854
|
-
m.y - HALF_SPAN2,
|
|
855
|
-
m.top,
|
|
856
|
-
mkInfo("top", m.top),
|
|
857
|
-
sidePaint,
|
|
858
|
-
palette,
|
|
859
|
-
isDark,
|
|
860
|
-
m,
|
|
861
|
-
activeKey,
|
|
862
|
-
fillMode
|
|
863
|
-
);
|
|
864
|
-
drawBox(
|
|
865
|
-
root,
|
|
866
|
-
m.x,
|
|
867
|
-
m.y + HALF_SPAN2,
|
|
868
|
-
m.bot,
|
|
869
|
-
mkInfo("bot", m.bot),
|
|
870
|
-
sidePaint,
|
|
871
|
-
palette,
|
|
872
|
-
isDark,
|
|
873
|
-
m,
|
|
874
|
-
activeKey,
|
|
875
|
-
fillMode
|
|
876
|
-
);
|
|
877
|
-
if (isUpset) drawUpset(root, m, sidePaint);
|
|
878
|
-
if (m.commentary.length) drawCommentary(root, m, palette);
|
|
879
|
-
}
|
|
880
|
-
}
|
|
881
|
-
function drawConnector(g, m, from, toCy, paint) {
|
|
882
|
-
if (!from) return;
|
|
883
|
-
const parentLeft = m.x - BOX_W / 2;
|
|
884
|
-
const parentRight = m.x + BOX_W / 2;
|
|
885
|
-
const childIsLeft = from.x < m.x;
|
|
886
|
-
const childEdge = childIsLeft ? from.x + BOX_W / 2 : from.x - BOX_W / 2;
|
|
887
|
-
const parentEdge = childIsLeft ? parentLeft : parentRight;
|
|
888
|
-
const midX = (childEdge + parentEdge) / 2;
|
|
889
|
-
const vLo = Math.min(from.yA, from.yB, toCy);
|
|
890
|
-
const vHi = Math.max(from.yA, from.yB, toCy);
|
|
891
|
-
const d = from.yA !== from.yB ? `M ${childEdge} ${from.yA} H ${midX} M ${childEdge} ${from.yB} H ${midX} M ${midX} ${vLo} V ${vHi} M ${midX} ${toCy} H ${parentEdge}` : `M ${childEdge} ${from.y} H ${midX} V ${toCy} H ${parentEdge}`;
|
|
892
|
-
g.append("path").attr("d", d).attr("fill", "none").attr("stroke", paint.wire).attr("stroke-width", 1.5);
|
|
893
|
-
}
|
|
894
|
-
function drawBox(g, cx, cy, name, info, sidePaint, palette, isDark, m, activeKey, fillMode) {
|
|
895
|
-
const x = cx - BOX_W / 2;
|
|
896
|
-
const y = cy - BOX_H / 2;
|
|
897
|
-
const paint = info.tagColor ? paintForAccent(palette, isDark, info.tagColor, fillMode) : sidePaint;
|
|
898
|
-
const cell = g.append("g").attr("class", "dgmo-bracket-box");
|
|
899
|
-
if (m.lineNumber !== null) cell.attr("data-line-number", m.lineNumber);
|
|
900
|
-
if (activeKey && info.tagValue)
|
|
901
|
-
cell.attr(`data-tag-${activeKey}`, info.tagValue.toLowerCase());
|
|
902
|
-
const isTBD = name === null;
|
|
903
|
-
let fill = "none";
|
|
904
|
-
let stroke = paint.hair;
|
|
905
|
-
let sw = 1.25;
|
|
906
|
-
let dash = null;
|
|
907
|
-
let textColor = paint.ink;
|
|
908
|
-
let weight = "bold";
|
|
909
|
-
if (info.isChampion) {
|
|
910
|
-
fill = paint.tint;
|
|
911
|
-
stroke = paint.accent;
|
|
912
|
-
sw = 3;
|
|
913
|
-
weight = "bold";
|
|
914
|
-
} else if (isTBD) {
|
|
915
|
-
fill = paint.track;
|
|
916
|
-
dash = "4 4";
|
|
917
|
-
textColor = paint.muted;
|
|
918
|
-
weight = "normal";
|
|
919
|
-
} else if (info.isWinner) {
|
|
920
|
-
fill = paint.tint;
|
|
921
|
-
stroke = paint.accent;
|
|
922
|
-
sw = 2;
|
|
923
|
-
weight = "bold";
|
|
924
|
-
} else if (info.isLoser) {
|
|
925
|
-
stroke = paint.hair;
|
|
926
|
-
textColor = paint.muted;
|
|
927
|
-
weight = "normal";
|
|
928
|
-
} else if (info.pending) {
|
|
929
|
-
stroke = mix(paint.accent, paint.hair, 50);
|
|
930
|
-
}
|
|
931
|
-
const onSolid = fillMode === "solid" && (info.isChampion || info.isWinner) ? contrastText(
|
|
932
|
-
paint.tint,
|
|
933
|
-
palette.textOnFillLight,
|
|
934
|
-
palette.textOnFillDark
|
|
935
|
-
) : null;
|
|
936
|
-
if (onSolid) textColor = onSolid;
|
|
937
|
-
const gutterColor = onSolid ?? paint.muted;
|
|
938
|
-
const rect = cell.append("rect").attr("x", x).attr("y", y).attr("width", BOX_W).attr("height", BOX_H).attr("rx", 6).attr("fill", fill).attr("stroke", stroke).attr("stroke-width", sw);
|
|
939
|
-
if (dash) rect.attr("stroke-dasharray", dash);
|
|
940
|
-
let leftPad = 11;
|
|
941
|
-
if (info.seed !== null) {
|
|
942
|
-
cell.append("text").attr("x", x + 9).attr("y", cy + 4).attr("text-anchor", "start").attr("font-size", 10).attr("font-weight", "bold").attr("fill", gutterColor).text(info.seed);
|
|
943
|
-
leftPad = 9 + Math.max(12, measureText(String(info.seed), 10, { bold: true })) + 6;
|
|
944
|
-
}
|
|
945
|
-
let rightPad = 11;
|
|
946
|
-
if (info.score) rightPad += 20;
|
|
947
|
-
if (info.isHome) {
|
|
948
|
-
drawHome(cell, x + BOX_W - rightPad - 6, cy, gutterColor);
|
|
949
|
-
rightPad += 16;
|
|
950
|
-
}
|
|
951
|
-
cell.append("text").attr("x", x + leftPad).attr("y", cy + 4).attr("text-anchor", "start").attr("font-size", 12.5).attr("font-weight", weight).attr("fill", textColor).text(
|
|
952
|
-
clip(
|
|
953
|
-
isTBD ? "TBD" : name,
|
|
954
|
-
BOX_W - leftPad - rightPad,
|
|
955
|
-
12.5,
|
|
956
|
-
weight === "bold"
|
|
957
|
-
)
|
|
958
|
-
);
|
|
959
|
-
if (info.score) {
|
|
960
|
-
cell.append("text").attr("x", x + BOX_W - 11).attr("y", cy + 4).attr("text-anchor", "end").attr("font-size", 12).attr("font-weight", info.isWinner ? "bold" : "normal").attr("fill", info.isWinner ? textColor : gutterColor).text(info.score);
|
|
961
|
-
}
|
|
962
|
-
}
|
|
963
|
-
function drawHome(cell, hx, cy, color) {
|
|
964
|
-
const s = 5;
|
|
965
|
-
const yTop = cy - s - 1;
|
|
966
|
-
const d = `M ${hx - s} ${cy - 1} L ${hx} ${yTop} L ${hx + s} ${cy - 1} L ${hx + s} ${cy + s} L ${hx - s} ${cy + s} Z`;
|
|
967
|
-
cell.append("path").attr("d", d).attr("fill", "none").attr("stroke", color).attr("stroke-width", 1.2).attr("stroke-linejoin", "round");
|
|
968
|
-
}
|
|
969
|
-
function drawUpset(g, m, paint) {
|
|
970
|
-
g.append("text").attr("x", m.x + BOX_W / 2).attr("y", m.y - HALF_SPAN2 - BOX_H / 2 - 4).attr("text-anchor", "end").attr("font-size", 9).attr("font-weight", "bold").attr("letter-spacing", 0.6).attr("fill", paint.accent).text("UPSET");
|
|
971
|
-
}
|
|
972
|
-
function drawCommentary(g, m, palette) {
|
|
973
|
-
const x = m.x - BOX_W / 2 + 2;
|
|
974
|
-
let y = m.y + HALF_SPAN2 + BOX_H / 2 + 13;
|
|
975
|
-
const muted = mix(palette.text, palette.bg, 45);
|
|
976
|
-
const bulletW = measureText("\u2022 ", 11);
|
|
977
|
-
const emit = (line, lx) => {
|
|
978
|
-
const t = g.append("text").attr("x", lx).attr("y", y).attr("text-anchor", "start").attr("font-size", 11).attr("font-style", "italic").attr("fill", muted);
|
|
979
|
-
renderInlineText(t, line, palette, 11);
|
|
980
|
-
y += COMMENT_LH;
|
|
981
|
-
};
|
|
982
|
-
for (const raw of m.commentary) {
|
|
983
|
-
const isBullet = raw.startsWith("\u2022 ");
|
|
984
|
-
if (isBullet) {
|
|
985
|
-
const body = raw.slice(2);
|
|
986
|
-
wrapText(body, COMMENT_W - bulletW, 11).forEach(
|
|
987
|
-
(line, i) => i === 0 ? emit(`\u2022 ${line}`, x) : emit(line, x + bulletW)
|
|
988
|
-
);
|
|
989
|
-
} else {
|
|
990
|
-
for (const line of wrapText(raw, COMMENT_W, 11)) emit(line, x);
|
|
991
|
-
}
|
|
992
|
-
}
|
|
993
|
-
}
|
|
994
|
-
function renderBracketForExport(container, parsed, palette, isDark, exportDims) {
|
|
995
|
-
renderBracket(container, parsed, palette, isDark, exportDims);
|
|
996
|
-
}
|
|
997
|
-
|
|
998
575
|
export {
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
576
|
+
BOX_W,
|
|
577
|
+
BOX_H,
|
|
578
|
+
PAIR_GAP,
|
|
579
|
+
COMMENT_W,
|
|
580
|
+
wrapText,
|
|
581
|
+
layoutBracket
|
|
1003
582
|
};
|
|
@@ -72,6 +72,8 @@ function escapeAttr(s) {
|
|
|
72
72
|
// src/timeline/renderer.ts
|
|
73
73
|
import * as d3Scale from "d3-scale";
|
|
74
74
|
import * as d3Selection from "d3-selection";
|
|
75
|
+
var MIN_TOP_SCALE_BAND = 26;
|
|
76
|
+
var MIN_BOTTOM_BAND = 30;
|
|
75
77
|
function renderEras(g, eras, scale, isVertical, innerWidth, innerHeight, onEnter, onLeave, hasScale = false, _tooltip = null, palette, reservedLabelY, fillMode, outlineBg) {
|
|
76
78
|
const eraColors = getEraColors(
|
|
77
79
|
palette ?? getPalette(DEFAULT_PALETTE_ID).light
|
|
@@ -799,11 +801,12 @@ function renderTimelineHorizontalTimeSort(container, parsed, palette, isDark, se
|
|
|
799
801
|
const MARKER_ROW_H = ctx.structural(22);
|
|
800
802
|
const eraReserve = timelineEras.length > 0 ? ERA_ROW_H : 0;
|
|
801
803
|
const markerReserve = timelineMarkers.length > 0 ? MARKER_ROW_H : 0;
|
|
802
|
-
const topScaleH = timelineScale ? ctx.structural(40) : 0;
|
|
804
|
+
const topScaleH = timelineScale ? Math.max(ctx.structural(40), MIN_TOP_SCALE_BAND) : 0;
|
|
805
|
+
const hasGroupLegend = timelineGroups.length > 0 && !parsed.noLegend;
|
|
803
806
|
const margin = {
|
|
804
|
-
top: ctx.aesthetic(
|
|
807
|
+
top: 50 + (hasGroupLegend ? 54 : ctx.aesthetic(54)) + topScaleH + eraReserve + markerReserve + tagLegendReserve,
|
|
805
808
|
right: ctx.aesthetic(40),
|
|
806
|
-
bottom: ctx.aesthetic(40) + scaleMargin,
|
|
809
|
+
bottom: Math.max(ctx.aesthetic(40), MIN_BOTTOM_BAND) + scaleMargin,
|
|
807
810
|
left: ctx.aesthetic(60)
|
|
808
811
|
};
|
|
809
812
|
const markerLabelY = markerReserve ? -(topScaleH + MARKER_ROW_H / 2) : 0;
|
|
@@ -874,8 +877,8 @@ function renderTimelineHorizontalTimeSort(container, parsed, palette, isDark, se
|
|
|
874
877
|
formatBoundaryLabel(latestEndDateStr, earliestStartDateStr)
|
|
875
878
|
);
|
|
876
879
|
}
|
|
877
|
-
if (
|
|
878
|
-
const legendY = timelineScale ? -
|
|
880
|
+
if (hasGroupLegend) {
|
|
881
|
+
const legendY = timelineScale ? -(topScaleH + 35) : -55;
|
|
879
882
|
renderTimelineGroupLegend(
|
|
880
883
|
g,
|
|
881
884
|
timelineGroups,
|
|
@@ -1033,7 +1036,7 @@ function renderTimelineHorizontalGrouped(container, parsed, palette, isDark, set
|
|
|
1033
1036
|
const MARKER_ROW_H = ctx.structural(22);
|
|
1034
1037
|
const eraReserve = timelineEras.length > 0 ? ERA_ROW_H : 0;
|
|
1035
1038
|
const markerReserve = timelineMarkers.length > 0 ? MARKER_ROW_H : 0;
|
|
1036
|
-
const topScaleH = timelineScale ? ctx.structural(40) : 0;
|
|
1039
|
+
const topScaleH = timelineScale ? Math.max(ctx.structural(40), MIN_TOP_SCALE_BAND) : 0;
|
|
1037
1040
|
const maxGroupNameLen = Math.max(...lanes.map((l) => l.name.length)) + 2;
|
|
1038
1041
|
const maxEventLabelLen = Math.max(
|
|
1039
1042
|
0,
|
|
@@ -1044,11 +1047,11 @@ function renderTimelineHorizontalGrouped(container, parsed, palette, isDark, set
|
|
|
1044
1047
|
ctx.aesthetic(140),
|
|
1045
1048
|
maxLeftLabelLen * sCharW + ctx.aesthetic(30)
|
|
1046
1049
|
);
|
|
1047
|
-
const baseTopMargin = title ?
|
|
1050
|
+
const baseTopMargin = title ? 50 : ctx.aesthetic(20);
|
|
1048
1051
|
const margin = {
|
|
1049
1052
|
top: baseTopMargin + topScaleH + eraReserve + markerReserve + tagLegendReserve,
|
|
1050
1053
|
right: ctx.aesthetic(40),
|
|
1051
|
-
bottom: ctx.aesthetic(40) + scaleMargin,
|
|
1054
|
+
bottom: Math.max(ctx.aesthetic(40), MIN_BOTTOM_BAND) + scaleMargin,
|
|
1052
1055
|
left: dynamicLeftMargin
|
|
1053
1056
|
};
|
|
1054
1057
|
const markerLabelY = markerReserve ? -(topScaleH + MARKER_ROW_H / 2) : 0;
|
|
@@ -1136,7 +1139,7 @@ function renderTimelineHorizontalGrouped(container, parsed, palette, isDark, set
|
|
|
1136
1139
|
g.append("rect").attr("class", "tl-group-header-bg").attr("data-group", lane.name).attr("x", bandX).attr("y", bandY).attr("width", bandW).attr("height", bandH).attr("rx", sBandRx).attr("fill", shapeFill(palette, laneColor, isDark, { mode: fillMode })).style("pointer-events", "none");
|
|
1137
1140
|
g.append("rect").attr("class", "tl-group-header-accent").attr("data-group", lane.name).attr("x", bandX).attr("y", bandY).attr("width", sBandAccentW).attr("height", bandH).attr("fill", laneColor).attr("clip-path", `url(#${clipId})`).style("pointer-events", "none");
|
|
1138
1141
|
const headerG = g.append("g").attr("class", "tl-group-header").attr("data-group", lane.name).style("cursor", "pointer").on("mouseenter", () => fadeToGroup(g, lane.name)).on("mouseleave", () => fadeReset(g)).on("click", () => toggleGroup(lane.name));
|
|
1139
|
-
headerG.append("text").attr("x",
|
|
1142
|
+
headerG.append("text").attr("x", bandX + 5).attr("y", curY + sBarH / 2).attr("dy", "0.35em").attr("text-anchor", "start").attr("fill", textColor).attr("font-size", sLaneHeaderFont).attr("font-weight", "bold").text(`${toggleIcon} ${lane.name}`);
|
|
1140
1143
|
if (lane.events.length > 0) {
|
|
1141
1144
|
const evDates = lane.events.map((ev) => parseTimelineDate(ev.date));
|
|
1142
1145
|
const evEndDates = lane.events.filter((ev) => ev.endDate).map((ev) => parseTimelineDate(ev.endDate));
|