@diagrammo/dgmo 0.93.0 → 0.95.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 +98 -21
- package/dist/advanced.js +79 -61
- package/dist/block.d.ts +6 -0
- package/dist/block.js +23 -20
- package/dist/chart-meta.js +9 -9
- package/dist/{charts-d3-ERYYVGP6.js → charts-d3-SZV2SFTM.js} +2 -2
- package/dist/{chunk-J6AEKIRQ.js → chunk-2I4UQG7W.js} +1 -1
- package/dist/{chunk-LDDDKWL6.js → chunk-2J2CWWWK.js} +1 -1
- package/dist/{chunk-QDRCWVV3.js → chunk-4EX4OKGZ.js} +71 -8
- package/dist/{chunk-A5WIAMCT.js → chunk-4UVCTLRH.js} +1 -1
- package/dist/{chunk-KCMGIYPO.js → chunk-5XBYLYYI.js} +1 -1
- package/dist/{chunk-XUTR7GNT.js → chunk-62GLKE6Q.js} +68 -29
- package/dist/{chunk-D6PVMUCX.js → chunk-67N26CER.js} +4 -4
- package/dist/{chunk-IIV4SMJL.js → chunk-6EFCOES2.js} +370 -285
- package/dist/{chunk-TNV4F5WA.js → chunk-7IALGIOI.js} +2 -2
- package/dist/{chunk-MB2SQF7N.js → chunk-7WEYXYLC.js} +1 -1
- package/dist/{chunk-4RY5D5RE.js → chunk-AM42TLSZ.js} +98 -93
- package/dist/{chunk-J43OCO22.js → chunk-BCV2VJTM.js} +2 -2
- package/dist/{chunk-7OWK5K5C.js → chunk-BH2SF4TT.js} +2 -2
- package/dist/{chunk-UVX3IYXF.js → chunk-BTP2BMWN.js} +4 -4
- package/dist/{chunk-MUJVNSEW.js → chunk-C6G36F5S.js} +1 -1
- package/dist/{chunk-ZTLMEBAC.js → chunk-E7F76M3U.js} +208 -26
- package/dist/{chunk-AUS7V54P.js → chunk-ERT7XDQW.js} +1 -1
- package/dist/{chunk-WK5QEYFA.js → chunk-EXHXTMVR.js} +3 -3
- package/dist/{chunk-YXXIRM4C.js → chunk-FWVE373X.js} +2 -2
- package/dist/{chunk-EQWC76GQ.js → chunk-GWQRTA5W.js} +2 -2
- package/dist/{chunk-GJXMG4S3.js → chunk-HHVIE45A.js} +1 -1
- package/dist/{chunk-4FT5KQ7H.js → chunk-IGJEKO2P.js} +1 -1
- package/dist/{chunk-KRU2LCIP.js → chunk-IHN6WDEY.js} +1 -1
- package/dist/{chunk-QT3KDDXK.js → chunk-IN7XI6NO.js} +0 -7
- package/dist/{chunk-NMK7KJZZ.js → chunk-IQN2NGEU.js} +4 -4
- package/dist/{chunk-M6ZQAFPD.js → chunk-IY62DXT2.js} +1 -1
- package/dist/{chunk-4WQX55VX.js → chunk-IZHGT5HD.js} +31 -5
- package/dist/{chunk-CYDM2XII.js → chunk-JJ7RGTAU.js} +56 -7
- package/dist/{chunk-NJJCTEBM.js → chunk-KLBTJEIK.js} +1 -1
- package/dist/{chunk-KHL652SX.js → chunk-KSZB7TIY.js} +1 -1
- package/dist/{chunk-KPSSNGAE.js → chunk-L5S32VZJ.js} +4 -4
- package/dist/{chunk-472EOHVW.js → chunk-LAXMUSGW.js} +31 -5
- package/dist/{chunk-M4NV5PPU.js → chunk-LJGE43SG.js} +1 -1
- package/dist/{chunk-VSOZZYLK.js → chunk-NERQSSYD.js} +20 -9
- package/dist/{chunk-UG7AQQNR.js → chunk-OSSTOSXU.js} +2 -2
- package/dist/{chunk-YDZIVDVR.js → chunk-P5XA627W.js} +7 -7
- package/dist/{chunk-DRQSB72S.js → chunk-PGBTUGDB.js} +2 -2
- package/dist/{chunk-65GPEV5V.js → chunk-QKPOIJBT.js} +2 -2
- package/dist/{chunk-QJOVZ4TC.js → chunk-QPQWY5RU.js} +1 -1
- package/dist/{chunk-P7TRFL4P.js → chunk-R46N5KR4.js} +2 -0
- package/dist/{chunk-LNVB75QU.js → chunk-RBVFNUAL.js} +1 -1
- package/dist/{chunk-DAHQL57W.js → chunk-RITTLIDJ.js} +1 -1
- package/dist/{chunk-2TFXRBCD.js → chunk-RQTDUZUH.js} +3 -3
- package/dist/{chunk-Q7Y6JQ3R.js → chunk-S4SJ4LLY.js} +1 -1
- package/dist/{chunk-LUDUDBO6.js → chunk-S5Y3OO3V.js} +2 -2
- package/dist/{chunk-3GCASUWA.js → chunk-TASRGDYP.js} +1 -1
- package/dist/{chunk-5HAYT43L.js → chunk-U6VS3T2M.js} +19 -3
- package/dist/{chunk-42UJ23H3.js → chunk-UIEKW4SG.js} +1 -0
- package/dist/{chunk-IESUM4N5.js → chunk-USE6HIOZ.js} +95 -62
- package/dist/{chunk-FIYW7JW4.js → chunk-USZLHYEQ.js} +1 -1
- package/dist/{chunk-U7U64UUN.js → chunk-V5D6GDNK.js} +4 -4
- package/dist/{chunk-ADVGV3P7.js → chunk-VB4VMB2I.js} +7 -5
- package/dist/{chunk-CQYFZOUC.js → chunk-VBMRZCMQ.js} +1 -1
- package/dist/{chunk-USAGHYRD.js → chunk-VF7IKIRS.js} +2 -2
- package/dist/{chunk-EJS3K44P.js → chunk-VGAMBOOC.js} +5 -3
- package/dist/{chunk-P5FUWXPW.js → chunk-VTESGA5F.js} +7 -5
- package/dist/chunk-WTTIODDV.js +9 -0
- package/dist/{chunk-5CBX66KB.js → chunk-XJQ6Z3FO.js} +2 -2
- package/dist/{chunk-KPGPK4SK.js → chunk-XOMIYMQ3.js} +6 -6
- package/dist/{chunk-KCHQVED5.js → chunk-YQMIKX54.js} +43 -7
- package/dist/completion-registry.js +3 -0
- package/dist/copy-image.d.ts +34 -0
- package/dist/copy-image.js +96 -0
- package/dist/countdown.js +1 -1
- package/dist/{dgmo-router-CZN36TR3.js → dgmo-router-6YLKM6WU.js} +13 -13
- package/dist/{dimensions-7HHKO5BS.js → dimensions-DS6FWLRY.js} +2 -2
- package/dist/{flowchart-parser-4MZPWYK6.js → flowchart-parser-J3VWIFWB.js} +5 -5
- package/dist/{flowchart-renderer-5XDN575E.js → flowchart-renderer-FECH5KK5.js} +14 -12
- package/dist/index.js +19 -19
- package/dist/layout-5SZ5A64I.js +24 -0
- package/dist/{layout-5GHV7HNM.js → layout-EWXHEXNS.js} +11 -10
- package/dist/{layout-QXGPY7KA.js → layout-TIBRMWHL.js} +7 -6
- package/dist/layout-VDCJOZSH.js +11 -0
- package/dist/layout-YBMT74MG.js +24 -0
- package/dist/{layout-search-EMTCDC3C.js → layout-search-AWPJWBKT.js} +9 -8
- package/dist/{parser-R3LPEQIT.js → parser-AR2GON37.js} +2 -2
- package/dist/{parser-YX72VLDZ.js → parser-BTSTKY5G.js} +5 -5
- package/dist/{parser-OGF2CDA6.js → parser-MA7L3XND.js} +5 -5
- package/dist/{parser-VKYUAT4Y.js → parser-ZRIZFDZY.js} +5 -5
- package/dist/{redraw-T324XAF3.js → redraw-AHW7EUUF.js} +2 -2
- package/dist/{redraw-MSXU4ZD2.js → redraw-STZ4WJAI.js} +3 -3
- package/dist/{renderer-HYGTMBHT.js → renderer-2LYX6NHN.js} +3 -3
- package/dist/{renderer-XJMVG2WJ.js → renderer-2QPHDBZY.js} +5 -5
- package/dist/{renderer-GPAVVUET.js → renderer-3NLDX7DQ.js} +3 -3
- package/dist/{renderer-IHCWBFPI.js → renderer-4VGO27EE.js} +14 -13
- package/dist/{renderer-JJ4QW3SK.js → renderer-6BDK47V6.js} +1 -1
- package/dist/{renderer-FL7EOSBU.js → renderer-7OGGV327.js} +3 -3
- package/dist/{renderer-5ITLW7MX.js → renderer-ACQQ2JXA.js} +6 -4
- package/dist/{renderer-37PLQHVB.js → renderer-BXD5KVSR.js} +2 -2
- package/dist/{renderer-TSCSZ6CL.js → renderer-HX6ICPDX.js} +2 -2
- package/dist/{renderer-BO73LSVT.js → renderer-IGSHD3OT.js} +4 -4
- package/dist/{renderer-DD6T3C4J.js → renderer-ISQJ2ELA.js} +2 -2
- package/dist/{renderer-CNRQFIEI.js → renderer-IVHD7ENY.js} +4 -4
- package/dist/{renderer-POBGKIUT.js → renderer-IXBOT6DU.js} +3 -3
- package/dist/{renderer-GFALLIYT.js → renderer-JYHA6R2D.js} +3 -3
- package/dist/{renderer-GOXAP2IE.js → renderer-KJ3V5BYP.js} +2 -2
- package/dist/{renderer-GFGFHYWY.js → renderer-MWZC7TMD.js} +2 -2
- package/dist/{renderer-3F47TPZ2.js → renderer-O6RP52CQ.js} +2 -2
- package/dist/{renderer-PBKTAWQV.js → renderer-P3WR7TKD.js} +14 -13
- package/dist/{renderer-VDBNC7A6.js → renderer-QTPWRRJ7.js} +2 -2
- package/dist/{renderer-J3QWK6IM.js → renderer-RUSQJFFC.js} +3 -3
- package/dist/{renderer-7Y3LR4R2.js → renderer-TINHO2IO.js} +2 -2
- package/dist/{renderer-J6H4Q3RM.js → renderer-UGW4N2W5.js} +4 -3
- package/dist/{renderer-EVKWGUGT.js → renderer-UTEEJDGB.js} +4 -3
- package/dist/{renderer-ISDKO3DI.js → renderer-WPN2SXGT.js} +2 -2
- package/dist/{renderer-6GJWADLO.js → renderer-XCLARXCK.js} +2 -2
- package/dist/{renderer-RDI2RYUP.js → renderer-XDX6S7OV.js} +3 -3
- package/dist/{renderer-VI34SD5V.js → renderer-Y47EM337.js} +6 -6
- package/dist/{renderer-HJ54YXT3.js → renderer-ZV6KCLCL.js} +14 -14
- package/dist/{state-parser-NPYTMRXP.js → state-parser-PMNHLHLE.js} +5 -5
- package/dist/{state-renderer-44K2KDX7.js → state-renderer-JQ5DS66J.js} +14 -13
- package/docs/language-reference.md +2 -2
- package/package.json +5 -1
- package/dist/layout-33FHGDEV.js +0 -23
- package/dist/layout-FTOGDNUL.js +0 -23
- package/dist/layout-J3AGFZZ7.js +0 -10
|
@@ -3,13 +3,13 @@ import {
|
|
|
3
3
|
} from "./chunk-R2K5L4UA.js";
|
|
4
4
|
import {
|
|
5
5
|
formatDateLabel
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-TASRGDYP.js";
|
|
7
7
|
import {
|
|
8
8
|
parseTimelineDate
|
|
9
9
|
} from "./chunk-BQO24BDM.js";
|
|
10
10
|
import {
|
|
11
11
|
layoutInlineHeader
|
|
12
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-UIEKW4SG.js";
|
|
13
13
|
import {
|
|
14
14
|
TITLE_FONT_SIZE,
|
|
15
15
|
TITLE_FONT_WEIGHT,
|
|
@@ -31,10 +31,10 @@ import {
|
|
|
31
31
|
} from "./chunk-JILEYZ2V.js";
|
|
32
32
|
import {
|
|
33
33
|
wrapDescriptionLines
|
|
34
|
-
} from "./chunk-
|
|
34
|
+
} from "./chunk-7WEYXYLC.js";
|
|
35
35
|
import {
|
|
36
36
|
renderInlineText
|
|
37
|
-
} from "./chunk-
|
|
37
|
+
} from "./chunk-QKPOIJBT.js";
|
|
38
38
|
import {
|
|
39
39
|
FONT_FAMILY
|
|
40
40
|
} from "./chunk-ICLB27PZ.js";
|
|
@@ -61,91 +61,34 @@ import {
|
|
|
61
61
|
|
|
62
62
|
// src/event-line/renderer.ts
|
|
63
63
|
import * as d3Selection from "d3-selection";
|
|
64
|
+
|
|
65
|
+
// src/event-line/layout.ts
|
|
64
66
|
var CARD_W = 210;
|
|
65
67
|
var H_MARGIN = 58;
|
|
66
|
-
var TITLE_AREA = 48;
|
|
67
|
-
var LEGEND_BAND = 34;
|
|
68
68
|
var LEADER_ABOVE = 46;
|
|
69
69
|
var LEADER_BELOW = 46;
|
|
70
70
|
var DESC_FONT = 11.5;
|
|
71
71
|
var DESC_LINE_H = 16;
|
|
72
72
|
var CARD_PAD = 9;
|
|
73
|
-
var CARD_BODY_TOP = HEADER_HEIGHT + SEPARATOR_GAP;
|
|
74
73
|
var TITLE_LINE_H = 16;
|
|
75
74
|
var TITLE_MAX_W = (CARD_W - CARD_PAD * 2) * 0.93;
|
|
76
|
-
var DOT_R = 5.5;
|
|
77
75
|
var MIN_SPACING = 96;
|
|
78
76
|
var LANE_GAP = 16;
|
|
79
77
|
var NEUTRAL_TAG = UNTAGGED_TAG_COLOR;
|
|
80
78
|
var CARD_INSET = 18;
|
|
81
79
|
var FAN_GAP = 6;
|
|
82
80
|
var DATE_SUBTITLE_H = 15;
|
|
83
|
-
var DATE_SUBTITLE_FONT = 10;
|
|
84
|
-
var SHELF_TINT = 13;
|
|
85
|
-
var SHELF_EDGE = 4;
|
|
86
|
-
var SHELF_EDGE_STYLE = "spine";
|
|
87
|
-
var SHELF_LIP = 2;
|
|
88
81
|
var ERA_BLOCK = 30;
|
|
89
|
-
var
|
|
82
|
+
var DOT_R = 5.5;
|
|
90
83
|
var ERA_BRACKET_PAD = 14;
|
|
91
84
|
var ERA_SEAM_GAP = 3;
|
|
92
|
-
var BREAK_HALF_H = 6;
|
|
93
|
-
var BREAK_AMP = 2.6;
|
|
94
|
-
var BREAK_GAP = 5;
|
|
95
85
|
var ERA_LEG = 13;
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
var COLLAPSED = "dgmo-evt-collapsed";
|
|
101
|
-
var OFF = "dgmo-evt-off";
|
|
102
|
-
var HIDDEN_ATTR = "data-evt-hidden";
|
|
103
|
-
var HOVER_CSS = `.dgmo-event-dot{transition:transform .12s ease;transform-box:fill-box;transform-origin:center}.dgmo-event-leader,.dgmo-event-card,.dgmo-event-era{transition:opacity .12s ease,filter .12s ease}.dgmo-event-dot,.dgmo-event-card,.dgmo-event-era{cursor:default}[data-legend-entry]{cursor:pointer}.${DIM}{opacity:.2}.dgmo-event-dot.${HL}{transform:scale(1.55)}.dgmo-event-leader.${HL}{stroke-opacity:1;stroke-width:2.5}.dgmo-event-card.${HL}{filter:drop-shadow(0 2px 5px rgba(0,0,0,.22))}.dgmo-event-era.${ERA_HL}{filter:drop-shadow(0 1px 2px rgba(0,0,0,.28))}.dgmo-event-card.${COLLAPSED},.dgmo-event-leader.${COLLAPSED}{opacity:0;pointer-events:none}.dgmo-event-card.${COLLAPSED}.${HL},.dgmo-event-leader.${COLLAPSED}.${HL}{opacity:1;pointer-events:auto}[data-legend-entry].${OFF} text{opacity:.5}`;
|
|
104
|
-
function renderEventLine(container, parsed, palette, isDark, onClickItem, exportDims, tagOverride, nowDate) {
|
|
105
|
-
if (parsed.events.length === 0) return;
|
|
106
|
-
d3Selection.select(container).selectAll(":not([data-d3-tooltip])").remove();
|
|
107
|
-
const width = exportDims?.width ?? container.clientWidth;
|
|
108
|
-
const heightHint = exportDims?.height ?? container.clientHeight;
|
|
109
|
-
if (width <= 0 || heightHint <= 0) return;
|
|
110
|
-
const exportMode = !!exportDims;
|
|
111
|
-
const hiddenSet = exportMode ? /* @__PURE__ */ new Set() : readHidden(container);
|
|
86
|
+
function layoutEventLine(parsed, palette, isDark, opts) {
|
|
87
|
+
const { width, heightHint, exportMode, hiddenSet, activeGroup, nowDate } = opts;
|
|
88
|
+
const header = layoutHeader(parsed, activeGroup, width, exportMode);
|
|
89
|
+
const { topUsed } = header;
|
|
112
90
|
const seriesColors = getSeriesColors(palette);
|
|
113
91
|
const accent = seriesColors[0];
|
|
114
|
-
const activeGroup = resolveActiveTagGroup(
|
|
115
|
-
parsed.tagGroups,
|
|
116
|
-
void 0,
|
|
117
|
-
tagOverride
|
|
118
|
-
);
|
|
119
|
-
const showTitle = !!parsed.title && !parsed.options.noTitle;
|
|
120
|
-
const hasLegend = parsed.tagGroups.length > 0 && activeGroup !== null && !parsed.options.noLegend;
|
|
121
|
-
const titleH = showTitle ? TITLE_AREA : 0;
|
|
122
|
-
const legendH = hasLegend ? LEGEND_BAND : 0;
|
|
123
|
-
const inlineRequested = parsed.options.legendInline === true;
|
|
124
|
-
const legendExtent = inlineRequested && hasLegend ? getLegendExtent(
|
|
125
|
-
{
|
|
126
|
-
groups: parsed.tagGroups,
|
|
127
|
-
position: {
|
|
128
|
-
placement: "top-center",
|
|
129
|
-
titleRelation: "inline-with-title"
|
|
130
|
-
},
|
|
131
|
-
mode: exportMode ? "export" : "preview"
|
|
132
|
-
},
|
|
133
|
-
{ activeGroup: activeGroup ?? null },
|
|
134
|
-
width
|
|
135
|
-
) : { width: 0, height: 0 };
|
|
136
|
-
const header = layoutInlineHeader({
|
|
137
|
-
requested: inlineRequested,
|
|
138
|
-
title: parsed.title ?? "",
|
|
139
|
-
hasLegend,
|
|
140
|
-
legendWidth: legendExtent.width,
|
|
141
|
-
legendHeight: legendExtent.height,
|
|
142
|
-
containerWidth: width,
|
|
143
|
-
titleBandHeight: titleH,
|
|
144
|
-
legendReserve: legendH,
|
|
145
|
-
titleBaselineY: TITLE_Y,
|
|
146
|
-
titleFontSize: TITLE_FONT_SIZE
|
|
147
|
-
});
|
|
148
|
-
const topUsed = header.inline ? titleH : titleH + legendH;
|
|
149
92
|
const eraByName = new Map(parsed.eras.map((e) => [e.name, e]));
|
|
150
93
|
const collapsedSet = new Set(
|
|
151
94
|
parsed.eras.filter((e) => e.collapsed).map((e) => e.name)
|
|
@@ -447,10 +390,10 @@ function renderEventLine(container, parsed, palette, isDark, onClickItem, export
|
|
|
447
390
|
const TOP_PAD = 14;
|
|
448
391
|
const BOT_PAD = 14;
|
|
449
392
|
const spineY2 = topUsed + TOP_PAD + aboveExt;
|
|
450
|
-
const
|
|
451
|
-
const
|
|
452
|
-
const totalH2 = exportMode ?
|
|
453
|
-
return { contentW: contentW2, contentH
|
|
393
|
+
const eraBaseY = eraSide === "above" ? spineY2 - (contentAbove + 14) : spineY2 + (contentBelow + 14);
|
|
394
|
+
const contentH = spineY2 + belowExt + BOT_PAD;
|
|
395
|
+
const totalH2 = exportMode ? contentH : Math.max(heightHint, contentH);
|
|
396
|
+
return { contentW: contentW2, contentH, totalH: totalH2, spineY: spineY2, eraBaseY, laneNear: laneNear2 };
|
|
454
397
|
};
|
|
455
398
|
const envStretch = Number(
|
|
456
399
|
globalThis.process?.env?.["DGMO_EVT_STRETCH"]
|
|
@@ -483,7 +426,307 @@ function renderEventLine(container, parsed, palette, isDark, onClickItem, export
|
|
|
483
426
|
}
|
|
484
427
|
}
|
|
485
428
|
}
|
|
486
|
-
const { contentW,
|
|
429
|
+
const { contentW, totalH, spineY, laneNear } = layout;
|
|
430
|
+
const spine = layoutSpine(placed, scaled);
|
|
431
|
+
const cards = layoutCards(placed, spineY, laneNear, spine);
|
|
432
|
+
return {
|
|
433
|
+
header,
|
|
434
|
+
placed,
|
|
435
|
+
scaled,
|
|
436
|
+
hasEras,
|
|
437
|
+
eraSide,
|
|
438
|
+
eraByName,
|
|
439
|
+
...layout,
|
|
440
|
+
cards,
|
|
441
|
+
spine,
|
|
442
|
+
eraBrackets: hasEras ? layoutEraBrackets(placed, eraByName, contentW) : [],
|
|
443
|
+
now: parsed.now && scaled ? layoutNowMarker(parsed, placed, cards, {
|
|
444
|
+
spineY,
|
|
445
|
+
contentW,
|
|
446
|
+
totalH,
|
|
447
|
+
topUsed,
|
|
448
|
+
nowDate
|
|
449
|
+
}) : null
|
|
450
|
+
};
|
|
451
|
+
}
|
|
452
|
+
var TITLE_AREA = 48;
|
|
453
|
+
var LEGEND_BAND = 34;
|
|
454
|
+
function layoutHeader(parsed, activeGroup, width, exportMode) {
|
|
455
|
+
const showTitle = !!parsed.title && !parsed.options.noTitle;
|
|
456
|
+
const hasLegend = parsed.tagGroups.length > 0 && activeGroup !== null && !parsed.options.noLegend;
|
|
457
|
+
const titleH = showTitle ? TITLE_AREA : 0;
|
|
458
|
+
const legendH = hasLegend ? LEGEND_BAND : 0;
|
|
459
|
+
const inlineRequested = parsed.options.legendInline === true;
|
|
460
|
+
const legendExtent = inlineRequested && hasLegend ? getLegendExtent(
|
|
461
|
+
{
|
|
462
|
+
groups: parsed.tagGroups,
|
|
463
|
+
position: {
|
|
464
|
+
placement: "top-center",
|
|
465
|
+
titleRelation: "inline-with-title"
|
|
466
|
+
},
|
|
467
|
+
mode: exportMode ? "export" : "preview"
|
|
468
|
+
},
|
|
469
|
+
{ activeGroup: activeGroup ?? null },
|
|
470
|
+
width
|
|
471
|
+
) : { width: 0, height: 0 };
|
|
472
|
+
const placement = layoutInlineHeader({
|
|
473
|
+
requested: inlineRequested,
|
|
474
|
+
title: parsed.title ?? "",
|
|
475
|
+
hasLegend,
|
|
476
|
+
legendWidth: legendExtent.width,
|
|
477
|
+
legendHeight: legendExtent.height,
|
|
478
|
+
containerWidth: width,
|
|
479
|
+
titleBandHeight: titleH,
|
|
480
|
+
legendReserve: legendH,
|
|
481
|
+
titleBaselineY: TITLE_Y,
|
|
482
|
+
titleFontSize: TITLE_FONT_SIZE
|
|
483
|
+
});
|
|
484
|
+
const topUsed = placement.inline ? titleH : titleH + legendH;
|
|
485
|
+
return { showTitle, hasLegend, titleH, topUsed, placement };
|
|
486
|
+
}
|
|
487
|
+
function eventTagHidden(metadata, hidden) {
|
|
488
|
+
if (hidden.size === 0) return false;
|
|
489
|
+
for (const [k, v] of Object.entries(metadata)) {
|
|
490
|
+
if (hidden.has(`${k.toLowerCase()}:${String(v).toLowerCase()}`))
|
|
491
|
+
return true;
|
|
492
|
+
}
|
|
493
|
+
return false;
|
|
494
|
+
}
|
|
495
|
+
function wrapDescription(lines, charsPerLine) {
|
|
496
|
+
return wrapDescriptionLines([...lines], charsPerLine);
|
|
497
|
+
}
|
|
498
|
+
function layoutSpine(placed, scaled) {
|
|
499
|
+
const x0 = Math.min(...placed.map((p) => p.x - p.spanHalf));
|
|
500
|
+
const x1 = Math.max(...placed.map((p) => p.x + p.spanHalf));
|
|
501
|
+
const left = x0 - 20;
|
|
502
|
+
const right = x1 + 20;
|
|
503
|
+
const dashed = scaled && placed.some((p) => p.future && !p.futureSpan);
|
|
504
|
+
const realRightX = placed.length ? Math.max(
|
|
505
|
+
left,
|
|
506
|
+
...placed.filter((p) => !p.future).map((p) => p.x + p.spanHalf)
|
|
507
|
+
) : right;
|
|
508
|
+
return { left, right, solidRight: dashed ? realRightX : right, dashed };
|
|
509
|
+
}
|
|
510
|
+
function layoutCards(placed, spineY, laneNear, spine) {
|
|
511
|
+
const boxes = placed.map((p) => {
|
|
512
|
+
const near = p.side === "above" ? spineY - laneNear(p) : spineY + laneNear(p);
|
|
513
|
+
const top = p.side === "above" ? near - p.cardH : near;
|
|
514
|
+
return { p, near, top };
|
|
515
|
+
});
|
|
516
|
+
const leaderCrossesBox = (owner, near) => {
|
|
517
|
+
const lo = Math.min(spineY, near);
|
|
518
|
+
const hi = Math.max(spineY, near);
|
|
519
|
+
return boxes.some(
|
|
520
|
+
(g) => g.p !== owner && owner.x > g.p.left && owner.x < g.p.left + CARD_W && hi > g.top && lo < g.top + g.p.cardH
|
|
521
|
+
);
|
|
522
|
+
};
|
|
523
|
+
const eraBarY = (p) => spineY + ERA_LEG * (p.side === "above" ? -1 : 1);
|
|
524
|
+
return boxes.map(({ p, near, top }) => {
|
|
525
|
+
let eraBar = null;
|
|
526
|
+
if (p.kind === "era") {
|
|
527
|
+
const half = Math.max(16, p.spanHalf);
|
|
528
|
+
eraBar = {
|
|
529
|
+
x0: Math.max(spine.left, p.x - half),
|
|
530
|
+
x1: Math.min(spine.right, p.x + half),
|
|
531
|
+
y: eraBarY(p)
|
|
532
|
+
};
|
|
533
|
+
}
|
|
534
|
+
return {
|
|
535
|
+
p,
|
|
536
|
+
near,
|
|
537
|
+
top,
|
|
538
|
+
leaderFrom: p.kind === "era" ? eraBarY(p) : spineY,
|
|
539
|
+
leaderCrossesCard: leaderCrossesBox(p, near),
|
|
540
|
+
eraBar
|
|
541
|
+
};
|
|
542
|
+
});
|
|
543
|
+
}
|
|
544
|
+
function layoutEraBrackets(placed, eraByName, contentW) {
|
|
545
|
+
const runs = [];
|
|
546
|
+
let prevName = null;
|
|
547
|
+
for (const p of placed) {
|
|
548
|
+
const name = p.eraName;
|
|
549
|
+
if (name && eraByName.has(name)) {
|
|
550
|
+
if (prevName === name && runs.length > 0) {
|
|
551
|
+
const r = runs[runs.length - 1];
|
|
552
|
+
r.firstX = Math.min(r.firstX, p.x);
|
|
553
|
+
r.lastX = Math.max(r.lastX, p.x);
|
|
554
|
+
} else {
|
|
555
|
+
runs.push({
|
|
556
|
+
era: eraByName.get(name),
|
|
557
|
+
collapsed: p.kind === "era",
|
|
558
|
+
firstX: p.x,
|
|
559
|
+
lastX: p.x
|
|
560
|
+
});
|
|
561
|
+
}
|
|
562
|
+
prevName = name;
|
|
563
|
+
} else {
|
|
564
|
+
prevName = null;
|
|
565
|
+
}
|
|
566
|
+
}
|
|
567
|
+
const clampX = (v) => Math.max(4, Math.min(contentW - 4, v));
|
|
568
|
+
const drawn = runs.filter((r) => !r.collapsed).map((r) => ({
|
|
569
|
+
era: r.era,
|
|
570
|
+
firstX: r.firstX,
|
|
571
|
+
lastX: r.lastX,
|
|
572
|
+
x0: clampX(r.firstX - ERA_BRACKET_PAD),
|
|
573
|
+
x1: clampX(r.lastX + ERA_BRACKET_PAD)
|
|
574
|
+
})).sort((a, b) => a.firstX - b.firstX);
|
|
575
|
+
for (let i = 1; i < drawn.length; i++) {
|
|
576
|
+
const prev = drawn[i - 1];
|
|
577
|
+
const cur = drawn[i];
|
|
578
|
+
if (prev.x1 <= cur.x0) continue;
|
|
579
|
+
const mid = (prev.lastX + cur.firstX) / 2;
|
|
580
|
+
prev.x1 = Math.max(prev.lastX, Math.min(prev.x1, mid - ERA_SEAM_GAP));
|
|
581
|
+
cur.x0 = Math.min(cur.firstX, Math.max(cur.x0, mid + ERA_SEAM_GAP));
|
|
582
|
+
}
|
|
583
|
+
return drawn;
|
|
584
|
+
}
|
|
585
|
+
function layoutNowMarker(parsed, placed, cards, frame) {
|
|
586
|
+
const now = parsed.now;
|
|
587
|
+
const { spineY, contentW, totalH, topUsed } = frame;
|
|
588
|
+
let nowValue = now.dateValue;
|
|
589
|
+
let nowIso = now.date;
|
|
590
|
+
if (now.computed) {
|
|
591
|
+
const d = frame.nowDate ?? /* @__PURE__ */ new Date();
|
|
592
|
+
const iso = `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, "0")}-${String(d.getDate()).padStart(2, "0")}`;
|
|
593
|
+
nowIso = iso;
|
|
594
|
+
const v = parseTimelineDate(iso);
|
|
595
|
+
nowValue = Number.isFinite(v) ? v : null;
|
|
596
|
+
}
|
|
597
|
+
const dated = placed.filter((p) => p.kind !== "era" && !p.future && p.dateValue !== null).sort((a, b) => a.dateValue - b.dateValue);
|
|
598
|
+
let nowX = nowValue === null ? null : valueToSpineX(nowValue, dated);
|
|
599
|
+
const lastReal = dated[dated.length - 1];
|
|
600
|
+
if (nowX !== null && nowValue !== null && lastReal && nowValue > lastReal.dateValue) {
|
|
601
|
+
const trailing = placed.filter((p) => p.future && !p.futureSpan).map((p) => p.x);
|
|
602
|
+
if (trailing.length) nowX = Math.min(...trailing);
|
|
603
|
+
}
|
|
604
|
+
if (nowX === null) return null;
|
|
605
|
+
const label = now.label ?? (nowIso ? formatDateLabel(nowIso) : "now");
|
|
606
|
+
const pillH = 18;
|
|
607
|
+
const pillW = Math.max(28, label.length * 6.6 + 16);
|
|
608
|
+
const PIN_GAP = 6;
|
|
609
|
+
const xHalf = pillW / 2 + 4;
|
|
610
|
+
const xLo = nowX - xHalf;
|
|
611
|
+
const xHi = nowX + xHalf;
|
|
612
|
+
const boxes = cards.map((g) => ({
|
|
613
|
+
side: g.p.side,
|
|
614
|
+
top: g.top,
|
|
615
|
+
bot: g.top + g.p.cardH,
|
|
616
|
+
left: g.p.left,
|
|
617
|
+
right: g.p.left + CARD_W
|
|
618
|
+
}));
|
|
619
|
+
const overlapsX = (b) => b.left < xHi && b.right > xLo;
|
|
620
|
+
const cands = [];
|
|
621
|
+
const addAbove = (pillBottom) => {
|
|
622
|
+
const y0 = pillBottom - pillH;
|
|
623
|
+
if (y0 > topUsed + 1)
|
|
624
|
+
cands.push({ y0, y1: pillBottom, stem: spineY - pillBottom });
|
|
625
|
+
};
|
|
626
|
+
const addBelow = (pillTop) => {
|
|
627
|
+
const y1 = pillTop + pillH;
|
|
628
|
+
if (y1 < totalH - 1)
|
|
629
|
+
cands.push({ y0: pillTop, y1, stem: pillTop - spineY });
|
|
630
|
+
};
|
|
631
|
+
addAbove(spineY - LEADER_ABOVE / 2 + pillH / 2);
|
|
632
|
+
addBelow(spineY + LEADER_BELOW / 2 - pillH / 2);
|
|
633
|
+
for (const b of boxes) {
|
|
634
|
+
if (!overlapsX(b)) continue;
|
|
635
|
+
if (b.side === "above") addAbove(b.top - PIN_GAP);
|
|
636
|
+
else addBelow(b.bot + PIN_GAP);
|
|
637
|
+
}
|
|
638
|
+
const isClear = (c) => !boxes.some((b) => overlapsX(b) && b.bot > c.y0 && b.top < c.y1);
|
|
639
|
+
const byStem = (a, b) => a.stem - b.stem;
|
|
640
|
+
const slot = cands.filter(isClear).sort(byStem)[0] ?? cands.sort(byStem)[0] ?? {
|
|
641
|
+
y0: spineY - PIN_GAP - pillH,
|
|
642
|
+
y1: spineY - PIN_GAP,
|
|
643
|
+
stem: PIN_GAP + pillH
|
|
644
|
+
};
|
|
645
|
+
const above = slot.y1 <= spineY;
|
|
646
|
+
return {
|
|
647
|
+
x: nowX,
|
|
648
|
+
label,
|
|
649
|
+
pillW,
|
|
650
|
+
pillH,
|
|
651
|
+
pillCX: Math.max(pillW / 2 + 2, Math.min(nowX, contentW - pillW / 2 - 2)),
|
|
652
|
+
y0: slot.y0,
|
|
653
|
+
y1: slot.y1,
|
|
654
|
+
stemFrom: above ? spineY - DOT_R : spineY + DOT_R,
|
|
655
|
+
stemTo: above ? slot.y1 : slot.y0
|
|
656
|
+
};
|
|
657
|
+
}
|
|
658
|
+
function valueToSpineX(v, dated) {
|
|
659
|
+
if (!dated.length) return null;
|
|
660
|
+
const first = dated[0];
|
|
661
|
+
const last = dated[dated.length - 1];
|
|
662
|
+
if (v <= first.dateValue) return first.x;
|
|
663
|
+
if (v >= last.dateValue) return last.x;
|
|
664
|
+
for (let i = 0; i < dated.length - 1; i++) {
|
|
665
|
+
const a = dated[i];
|
|
666
|
+
const b = dated[i + 1];
|
|
667
|
+
if (v >= a.dateValue && v <= b.dateValue) {
|
|
668
|
+
const span = b.dateValue - a.dateValue;
|
|
669
|
+
if (span <= 0) return a.x;
|
|
670
|
+
return a.x + (v - a.dateValue) / span * (b.x - a.x);
|
|
671
|
+
}
|
|
672
|
+
}
|
|
673
|
+
return last.x;
|
|
674
|
+
}
|
|
675
|
+
|
|
676
|
+
// src/event-line/renderer.ts
|
|
677
|
+
var CARD_BODY_TOP = HEADER_HEIGHT + SEPARATOR_GAP;
|
|
678
|
+
var DATE_SUBTITLE_FONT = 10;
|
|
679
|
+
var SHELF_TINT = 13;
|
|
680
|
+
var SHELF_EDGE = 4;
|
|
681
|
+
var SHELF_EDGE_STYLE = "spine";
|
|
682
|
+
var SHELF_LIP = 2;
|
|
683
|
+
var ERA_BRACKET_CAP = 8;
|
|
684
|
+
var BREAK_HALF_H = 6;
|
|
685
|
+
var BREAK_AMP = 2.6;
|
|
686
|
+
var BREAK_GAP = 5;
|
|
687
|
+
var ERA_LABEL_FONT = 11.5;
|
|
688
|
+
var HL = "dgmo-evt-hl";
|
|
689
|
+
var DIM = "dgmo-evt-dim";
|
|
690
|
+
var ERA_HL = "dgmo-evt-era-hl";
|
|
691
|
+
var COLLAPSED = "dgmo-evt-collapsed";
|
|
692
|
+
var OFF = "dgmo-evt-off";
|
|
693
|
+
var HIDDEN_ATTR = "data-evt-hidden";
|
|
694
|
+
var HOVER_CSS = `.dgmo-event-dot{transition:transform .12s ease;transform-box:fill-box;transform-origin:center}.dgmo-event-leader,.dgmo-event-card,.dgmo-event-era{transition:opacity .12s ease,filter .12s ease}.dgmo-event-dot,.dgmo-event-card,.dgmo-event-era{cursor:default}[data-legend-entry]{cursor:pointer}.${DIM}{opacity:.2}.dgmo-event-dot.${HL}{transform:scale(1.55)}.dgmo-event-leader.${HL}{stroke-opacity:1;stroke-width:2.5}.dgmo-event-card.${HL}{filter:drop-shadow(0 2px 5px rgba(0,0,0,.22))}.dgmo-event-era.${ERA_HL}{filter:drop-shadow(0 1px 2px rgba(0,0,0,.28))}.dgmo-event-card.${COLLAPSED},.dgmo-event-leader.${COLLAPSED}{opacity:0;pointer-events:none}.dgmo-event-card.${COLLAPSED}.${HL},.dgmo-event-leader.${COLLAPSED}.${HL}{opacity:1;pointer-events:auto}[data-legend-entry].${OFF} text{opacity:.5}`;
|
|
695
|
+
function renderEventLine(container, parsed, palette, isDark, onClickItem, exportDims, tagOverride, nowDate) {
|
|
696
|
+
if (parsed.events.length === 0) return;
|
|
697
|
+
d3Selection.select(container).selectAll(":not([data-d3-tooltip])").remove();
|
|
698
|
+
const width = exportDims?.width ?? container.clientWidth;
|
|
699
|
+
const heightHint = exportDims?.height ?? container.clientHeight;
|
|
700
|
+
if (width <= 0 || heightHint <= 0) return;
|
|
701
|
+
const exportMode = !!exportDims;
|
|
702
|
+
const hiddenSet = exportMode ? /* @__PURE__ */ new Set() : readHidden(container);
|
|
703
|
+
const activeGroup = resolveActiveTagGroup(
|
|
704
|
+
parsed.tagGroups,
|
|
705
|
+
void 0,
|
|
706
|
+
tagOverride
|
|
707
|
+
);
|
|
708
|
+
const {
|
|
709
|
+
header: band,
|
|
710
|
+
hasEras,
|
|
711
|
+
eraSide,
|
|
712
|
+
contentW,
|
|
713
|
+
contentH,
|
|
714
|
+
totalH,
|
|
715
|
+
spineY,
|
|
716
|
+
eraBaseY,
|
|
717
|
+
cards,
|
|
718
|
+
spine,
|
|
719
|
+
eraBrackets,
|
|
720
|
+
now
|
|
721
|
+
} = layoutEventLine(parsed, palette, isDark, {
|
|
722
|
+
width,
|
|
723
|
+
heightHint,
|
|
724
|
+
exportMode,
|
|
725
|
+
hiddenSet,
|
|
726
|
+
activeGroup,
|
|
727
|
+
nowDate
|
|
728
|
+
});
|
|
729
|
+
const { showTitle, hasLegend, titleH, topUsed, placement: header } = band;
|
|
487
730
|
const svg = d3Selection.select(container).append("svg").attr("width", contentW).attr("height", totalH).attr("viewBox", `0 0 ${contentW} ${totalH}`).attr("preserveAspectRatio", "xMidYMin meet").attr("xmlns", "http://www.w3.org/2000/svg").style("font-family", FONT_FAMILY);
|
|
488
731
|
if (!exportMode && totalH > contentH) {
|
|
489
732
|
svg.attr("data-content-height", Math.ceil(contentH));
|
|
@@ -525,43 +768,16 @@ function renderEventLine(container, parsed, palette, isDark, onClickItem, export
|
|
|
525
768
|
});
|
|
526
769
|
if (!exportMode) addLegendEntryHitAreas(legendG);
|
|
527
770
|
}
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
const spineRight = x1 + 20;
|
|
532
|
-
const hasTrailingFuture = scaled && placed.some((p) => p.future && !p.futureSpan);
|
|
533
|
-
const realRightX = placed.length ? Math.max(
|
|
534
|
-
spineLeft,
|
|
535
|
-
...placed.filter((p) => !p.future).map((p) => p.x + p.spanHalf)
|
|
536
|
-
) : spineRight;
|
|
537
|
-
const solidRight = hasTrailingFuture ? realRightX : spineRight;
|
|
538
|
-
svg.append("line").attr("x1", spineLeft).attr("y1", spineY).attr("x2", solidRight).attr("y2", spineY).attr("stroke", palette.text).attr("stroke-width", 2.5).attr("stroke-linecap", "round");
|
|
539
|
-
if (hasTrailingFuture) {
|
|
540
|
-
svg.append("line").attr("x1", solidRight).attr("y1", spineY).attr("x2", spineRight).attr("y2", spineY).attr("stroke", palette.text).attr("stroke-width", 2.5).attr("stroke-linecap", "round").attr("stroke-opacity", 0.45).attr("stroke-dasharray", HAIRLINE_DASH);
|
|
771
|
+
svg.append("line").attr("x1", spine.left).attr("y1", spineY).attr("x2", spine.solidRight).attr("y2", spineY).attr("stroke", palette.text).attr("stroke-width", 2.5).attr("stroke-linecap", "round");
|
|
772
|
+
if (spine.dashed) {
|
|
773
|
+
svg.append("line").attr("x1", spine.solidRight).attr("y1", spineY).attr("x2", spine.right).attr("y2", spineY).attr("stroke", palette.text).attr("stroke-width", 2.5).attr("stroke-linecap", "round").attr("stroke-opacity", 0.45).attr("stroke-dasharray", HAIRLINE_DASH);
|
|
541
774
|
}
|
|
542
|
-
const
|
|
543
|
-
const
|
|
544
|
-
const top = p.side === "above" ? near - p.cardH : near;
|
|
545
|
-
return { p, near, top };
|
|
546
|
-
});
|
|
547
|
-
const leaderCrossesBox = (owner, near) => {
|
|
548
|
-
const lo = Math.min(spineY, near);
|
|
549
|
-
const hi = Math.max(spineY, near);
|
|
550
|
-
return geo.some(
|
|
551
|
-
(g) => g.p !== owner && owner.x > g.p.left && owner.x < g.p.left + CARD_W && hi > g.top && lo < g.top + g.p.cardH
|
|
552
|
-
);
|
|
553
|
-
};
|
|
554
|
-
const eraBarY = (p) => spineY + ERA_LEG * (p.side === "above" ? -1 : 1);
|
|
555
|
-
for (const { p, near } of geo) {
|
|
556
|
-
const spineEnd = p.kind === "era" ? eraBarY(p) : spineY;
|
|
557
|
-
const leader = svg.append("line").attr("class", "dgmo-event-leader").attr("x1", p.x).attr("y1", spineEnd).attr("x2", p.x).attr("y2", near).attr("stroke", p.color).attr("stroke-width", 1.5).attr(
|
|
558
|
-
"stroke-opacity",
|
|
559
|
-
leaderCrossesBox(p, near) ? 0.18 : p.future ? 0.4 : 0.65
|
|
560
|
-
);
|
|
775
|
+
for (const { p, near, leaderFrom, leaderCrossesCard } of cards) {
|
|
776
|
+
const leader = svg.append("line").attr("class", "dgmo-event-leader").attr("x1", p.x).attr("y1", leaderFrom).attr("x2", p.x).attr("y2", near).attr("stroke", p.color).attr("stroke-width", 1.5).attr("stroke-opacity", leaderCrossesCard ? 0.18 : p.future ? 0.4 : 0.65);
|
|
561
777
|
applyHoverHooks(leader, p);
|
|
562
778
|
}
|
|
563
779
|
let shelfClipSeq = 0;
|
|
564
|
-
for (const { p, top } of
|
|
780
|
+
for (const { p, top } of cards) {
|
|
565
781
|
const left = p.left;
|
|
566
782
|
const cardG = svg.append("g").attr("class", "dgmo-event-card").attr("transform", `translate(${left}, ${top})`).attr("data-line-number", p.lineNumber);
|
|
567
783
|
applyHoverHooks(cardG, p);
|
|
@@ -659,15 +875,10 @@ function renderEventLine(container, parsed, palette, isDark, onClickItem, export
|
|
|
659
875
|
}
|
|
660
876
|
}
|
|
661
877
|
}
|
|
662
|
-
for (const p of
|
|
663
|
-
if (
|
|
878
|
+
for (const { p, eraBar } of cards) {
|
|
879
|
+
if (eraBar) {
|
|
664
880
|
const col = p.color;
|
|
665
|
-
const
|
|
666
|
-
const spineLeft2 = x0 - 20;
|
|
667
|
-
const spineRight2 = x1 + 20;
|
|
668
|
-
const bx0 = Math.max(spineLeft2, p.x - half);
|
|
669
|
-
const bx1 = Math.min(spineRight2, p.x + half);
|
|
670
|
-
const barY = eraBarY(p);
|
|
881
|
+
const { x0: bx0, x1: bx1, y: barY } = eraBar;
|
|
671
882
|
const eg = svg.append("g").attr("class", "dgmo-event-era").attr("data-era", p.era.name).attr("data-era-collapsed", "true").attr("data-line-number", p.lineNumber);
|
|
672
883
|
if (onClickItem) {
|
|
673
884
|
const hy0 = Math.min(barY, spineY) - 4;
|
|
@@ -693,52 +904,17 @@ function renderEventLine(container, parsed, palette, isDark, onClickItem, export
|
|
|
693
904
|
applyHoverHooks(dot, p);
|
|
694
905
|
}
|
|
695
906
|
if (hasEras) {
|
|
696
|
-
const runs = [];
|
|
697
|
-
let prevName = null;
|
|
698
|
-
for (const p of placed) {
|
|
699
|
-
const name = p.eraName;
|
|
700
|
-
if (name && eraByName.has(name)) {
|
|
701
|
-
if (prevName === name && runs.length > 0) {
|
|
702
|
-
const r = runs[runs.length - 1];
|
|
703
|
-
r.firstX = Math.min(r.firstX, p.x);
|
|
704
|
-
r.lastX = Math.max(r.lastX, p.x);
|
|
705
|
-
} else {
|
|
706
|
-
runs.push({
|
|
707
|
-
era: eraByName.get(name),
|
|
708
|
-
collapsed: p.kind === "era",
|
|
709
|
-
firstX: p.x,
|
|
710
|
-
lastX: p.x
|
|
711
|
-
});
|
|
712
|
-
}
|
|
713
|
-
prevName = name;
|
|
714
|
-
} else {
|
|
715
|
-
prevName = null;
|
|
716
|
-
}
|
|
717
|
-
}
|
|
718
|
-
const clampX = (v) => Math.max(4, Math.min(contentW - 4, v));
|
|
719
907
|
const cap = eraSide === "above" ? 1 : -1;
|
|
720
|
-
const
|
|
721
|
-
|
|
722
|
-
x0: clampX(r.firstX - ERA_BRACKET_PAD),
|
|
723
|
-
x1: clampX(r.lastX + ERA_BRACKET_PAD)
|
|
724
|
-
})).sort((a, b) => a.run.firstX - b.run.firstX);
|
|
725
|
-
for (let i = 1; i < drawn.length; i++) {
|
|
726
|
-
const prev = drawn[i - 1];
|
|
727
|
-
const cur = drawn[i];
|
|
728
|
-
if (prev.x1 <= cur.x0) continue;
|
|
729
|
-
const mid = (prev.run.lastX + cur.run.firstX) / 2;
|
|
730
|
-
prev.x1 = Math.max(prev.run.lastX, Math.min(prev.x1, mid - ERA_SEAM_GAP));
|
|
731
|
-
cur.x0 = Math.min(cur.run.firstX, Math.max(cur.x0, mid + ERA_SEAM_GAP));
|
|
732
|
-
}
|
|
733
|
-
for (const { run: r, x0: x02, x1: x12 } of drawn) {
|
|
908
|
+
for (const r of eraBrackets) {
|
|
909
|
+
const { x0, x1 } = r;
|
|
734
910
|
const neutral = !r.era.color;
|
|
735
911
|
const col = neutral ? palette.text : resolveColor(r.era.color, palette) ?? palette.text;
|
|
736
912
|
const op = neutral ? 0.5 : 0.85;
|
|
737
913
|
const y = eraBaseY;
|
|
738
|
-
const eg = svg.append("g").attr("class", "dgmo-event-era").attr("data-era", r.era.name).attr("data-era-collapsed",
|
|
914
|
+
const eg = svg.append("g").attr("class", "dgmo-event-era").attr("data-era", r.era.name).attr("data-era-collapsed", "false").attr("data-line-number", r.era.lineNumber);
|
|
739
915
|
eg.append("path").attr(
|
|
740
916
|
"d",
|
|
741
|
-
`M${
|
|
917
|
+
`M${x0},${y + ERA_BRACKET_CAP * cap} L${x0},${y} L${x1},${y} L${x1},${y + ERA_BRACKET_CAP * cap}`
|
|
742
918
|
).attr("fill", "none").attr("stroke", col).attr("stroke-width", 1.5).attr("stroke-linecap", "round").attr("stroke-linejoin", "round").attr("stroke-opacity", op);
|
|
743
919
|
eg.append("text").attr("x", (r.firstX + r.lastX) / 2).attr("y", eraSide === "above" ? y - 6 : y + ERA_LABEL_FONT + 4).attr("text-anchor", "middle").attr("font-family", FONT_FAMILY).attr("font-size", ERA_LABEL_FONT).attr("font-weight", "bold").attr("fill", col).attr("fill-opacity", neutral ? 0.85 : 1).text(r.era.name);
|
|
744
920
|
if (onClickItem) {
|
|
@@ -747,110 +923,47 @@ function renderEventLine(container, parsed, palette, isDark, onClickItem, export
|
|
|
747
923
|
}
|
|
748
924
|
}
|
|
749
925
|
}
|
|
750
|
-
if (parsed.now &&
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
const
|
|
780
|
-
|
|
781
|
-
const
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
right: g.p.left + CARD_W
|
|
792
|
-
}));
|
|
793
|
-
const overlapsX = (b) => b.left < xHi && b.right > xLo;
|
|
794
|
-
const cands = [];
|
|
795
|
-
const addAbove = (pillBottom) => {
|
|
796
|
-
const y0 = pillBottom - pillH;
|
|
797
|
-
if (y0 > topUsed + 1)
|
|
798
|
-
cands.push({ y0, y1: pillBottom, stem: spineY - pillBottom });
|
|
799
|
-
};
|
|
800
|
-
const addBelow = (pillTop) => {
|
|
801
|
-
const y1 = pillTop + pillH;
|
|
802
|
-
if (y1 < totalH - 1)
|
|
803
|
-
cands.push({ y0: pillTop, y1, stem: pillTop - spineY });
|
|
804
|
-
};
|
|
805
|
-
addAbove(spineY - LEADER_ABOVE / 2 + pillH / 2);
|
|
806
|
-
addBelow(spineY + LEADER_BELOW / 2 - pillH / 2);
|
|
807
|
-
for (const b of boxes) {
|
|
808
|
-
if (!overlapsX(b)) continue;
|
|
809
|
-
if (b.side === "above") addAbove(b.top - PIN_GAP);
|
|
810
|
-
else addBelow(b.bot + PIN_GAP);
|
|
811
|
-
}
|
|
812
|
-
const isClear = (c) => !boxes.some((b) => overlapsX(b) && b.bot > c.y0 && b.top < c.y1);
|
|
813
|
-
const byStem = (a, b) => a.stem - b.stem;
|
|
814
|
-
const slot = cands.filter(isClear).sort(byStem)[0] ?? cands.sort(byStem)[0] ?? {
|
|
815
|
-
y0: spineY - PIN_GAP - pillH,
|
|
816
|
-
y1: spineY - PIN_GAP,
|
|
817
|
-
stem: PIN_GAP + pillH
|
|
818
|
-
};
|
|
819
|
-
const above = slot.y1 <= spineY;
|
|
820
|
-
const stemFrom = above ? spineY - DOT_R : spineY + DOT_R;
|
|
821
|
-
const stemTo = above ? slot.y1 : slot.y0;
|
|
822
|
-
const pillCX = Math.max(
|
|
823
|
-
pillW / 2 + 2,
|
|
824
|
-
Math.min(nowX, contentW - pillW / 2 - 2)
|
|
825
|
-
);
|
|
826
|
-
const nowG = svg.append("g").attr("class", "evt-now").attr("data-now-x", nowX.toFixed(2));
|
|
827
|
-
const FADE_SPAN = Math.min(LEADER_ABOVE, LEADER_BELOW);
|
|
828
|
-
const ruleTop = spineY - FADE_SPAN;
|
|
829
|
-
const ruleBot = spineY + FADE_SPAN;
|
|
830
|
-
const gradId = `evt-now-fade-${nowX.toFixed(0)}`;
|
|
831
|
-
const grad = nowG.append("defs").append("linearGradient").attr("id", gradId).attr("gradientUnits", "userSpaceOnUse").attr("x1", 0).attr("y1", ruleTop).attr("x2", 0).attr("y2", ruleBot);
|
|
832
|
-
grad.append("stop").attr("offset", "0%").attr("stop-color", nowColor).attr("stop-opacity", 0);
|
|
833
|
-
grad.append("stop").attr("offset", "50%").attr("stop-color", nowColor).attr("stop-opacity", 0.9);
|
|
834
|
-
grad.append("stop").attr("offset", "100%").attr("stop-color", nowColor).attr("stop-opacity", 0);
|
|
835
|
-
nowG.append("line").attr("x1", nowX).attr("y1", ruleTop).attr("x2", nowX).attr("y2", ruleBot).attr("stroke", `url(#${gradId})`).attr("stroke-width", 1.6).attr("stroke-dasharray", HAIRLINE_DASH).attr("stroke-linecap", "round");
|
|
836
|
-
nowG.append("path").attr("d", `M${nowX} ${spineY - 5} l5 5 l-5 5 l-5 -5 z`).attr("fill", nowColor);
|
|
837
|
-
nowG.append("line").attr("x1", nowX).attr("y1", stemFrom).attr("x2", nowX).attr("y2", stemTo).attr("stroke", nowColor).attr("stroke-width", 1.6);
|
|
838
|
-
nowG.append("rect").attr("x", pillCX - pillW / 2).attr("y", slot.y0).attr("width", pillW).attr("height", pillH).attr("rx", pillH / 2).attr("fill", pillFill).attr("stroke", pillStroke).attr("stroke-width", pillStrokeW);
|
|
839
|
-
nowG.append("text").attr("x", pillCX).attr("y", slot.y0 + pillH / 2).attr("text-anchor", "middle").attr("dominant-baseline", "central").attr("font-family", FONT_FAMILY).attr("font-size", 10.5).attr("font-weight", "bold").attr("fill", pillText).text(label);
|
|
840
|
-
if (!exportMode) {
|
|
841
|
-
const hoverRule = nowG.append("line").attr("class", "evt-now-rule-full").attr("x1", nowX).attr("y1", Math.max(topUsed, 2)).attr("x2", nowX).attr("y2", totalH - 2).attr("stroke", nowColor).attr("stroke-width", 1.4).attr("stroke-dasharray", HAIRLINE_DASH).attr("stroke-linecap", "round").attr("opacity", 0).style("transition", "opacity 120ms ease").style("pointer-events", "none");
|
|
842
|
-
hoverRule.lower();
|
|
843
|
-
const nowNode = nowG.node();
|
|
844
|
-
if (nowNode) {
|
|
845
|
-
nowNode.addEventListener(
|
|
846
|
-
"mouseenter",
|
|
847
|
-
() => hoverRule.attr("opacity", 0.5)
|
|
848
|
-
);
|
|
849
|
-
nowNode.addEventListener(
|
|
850
|
-
"mouseleave",
|
|
851
|
-
() => hoverRule.attr("opacity", 0)
|
|
852
|
-
);
|
|
853
|
-
}
|
|
926
|
+
if (parsed.now && now) {
|
|
927
|
+
const nowX = now.x;
|
|
928
|
+
const base = themeBaseBg(palette, isDark);
|
|
929
|
+
const nowColor = parsed.now.color ? resolveColor(parsed.now.color, palette) ?? palette.destructive : palette.destructive;
|
|
930
|
+
const fillMode = parsed.options.fillMode;
|
|
931
|
+
const pillFill = shapeFill(palette, nowColor, isDark, { mode: fillMode });
|
|
932
|
+
const pillStroke = fillMode === "solid" ? "none" : fillMode === "outline" ? nowColor : mix(nowColor, base, 55);
|
|
933
|
+
const pillStrokeW = fillMode === "solid" ? 0 : fillMode === "outline" ? 1.3 : 1;
|
|
934
|
+
const pillText = fillMode === "solid" ? contrastText(
|
|
935
|
+
pillFill,
|
|
936
|
+
palette.textOnFillLight,
|
|
937
|
+
palette.textOnFillDark
|
|
938
|
+
) : nowColor;
|
|
939
|
+
const { label, pillW, pillH, pillCX, y0, stemFrom, stemTo } = now;
|
|
940
|
+
const nowG = svg.append("g").attr("class", "evt-now").attr("data-now-x", nowX.toFixed(2));
|
|
941
|
+
const FADE_SPAN = Math.min(LEADER_ABOVE, LEADER_BELOW);
|
|
942
|
+
const ruleTop = spineY - FADE_SPAN;
|
|
943
|
+
const ruleBot = spineY + FADE_SPAN;
|
|
944
|
+
const gradId = `evt-now-fade-${nowX.toFixed(0)}`;
|
|
945
|
+
const grad = nowG.append("defs").append("linearGradient").attr("id", gradId).attr("gradientUnits", "userSpaceOnUse").attr("x1", 0).attr("y1", ruleTop).attr("x2", 0).attr("y2", ruleBot);
|
|
946
|
+
grad.append("stop").attr("offset", "0%").attr("stop-color", nowColor).attr("stop-opacity", 0);
|
|
947
|
+
grad.append("stop").attr("offset", "50%").attr("stop-color", nowColor).attr("stop-opacity", 0.9);
|
|
948
|
+
grad.append("stop").attr("offset", "100%").attr("stop-color", nowColor).attr("stop-opacity", 0);
|
|
949
|
+
nowG.append("line").attr("x1", nowX).attr("y1", ruleTop).attr("x2", nowX).attr("y2", ruleBot).attr("stroke", `url(#${gradId})`).attr("stroke-width", 1.6).attr("stroke-dasharray", HAIRLINE_DASH).attr("stroke-linecap", "round");
|
|
950
|
+
nowG.append("path").attr("d", `M${nowX} ${spineY - 5} l5 5 l-5 5 l-5 -5 z`).attr("fill", nowColor);
|
|
951
|
+
nowG.append("line").attr("x1", nowX).attr("y1", stemFrom).attr("x2", nowX).attr("y2", stemTo).attr("stroke", nowColor).attr("stroke-width", 1.6);
|
|
952
|
+
nowG.append("rect").attr("x", pillCX - pillW / 2).attr("y", y0).attr("width", pillW).attr("height", pillH).attr("rx", pillH / 2).attr("fill", pillFill).attr("stroke", pillStroke).attr("stroke-width", pillStrokeW);
|
|
953
|
+
nowG.append("text").attr("x", pillCX).attr("y", y0 + pillH / 2).attr("text-anchor", "middle").attr("dominant-baseline", "central").attr("font-family", FONT_FAMILY).attr("font-size", 10.5).attr("font-weight", "bold").attr("fill", pillText).text(label);
|
|
954
|
+
if (!exportMode) {
|
|
955
|
+
const hoverRule = nowG.append("line").attr("class", "evt-now-rule-full").attr("x1", nowX).attr("y1", Math.max(topUsed, 2)).attr("x2", nowX).attr("y2", totalH - 2).attr("stroke", nowColor).attr("stroke-width", 1.4).attr("stroke-dasharray", HAIRLINE_DASH).attr("stroke-linecap", "round").attr("opacity", 0).style("transition", "opacity 120ms ease").style("pointer-events", "none");
|
|
956
|
+
hoverRule.lower();
|
|
957
|
+
const nowNode = nowG.node();
|
|
958
|
+
if (nowNode) {
|
|
959
|
+
nowNode.addEventListener(
|
|
960
|
+
"mouseenter",
|
|
961
|
+
() => hoverRule.attr("opacity", 0.5)
|
|
962
|
+
);
|
|
963
|
+
nowNode.addEventListener(
|
|
964
|
+
"mouseleave",
|
|
965
|
+
() => hoverRule.attr("opacity", 0)
|
|
966
|
+
);
|
|
854
967
|
}
|
|
855
968
|
}
|
|
856
969
|
}
|
|
@@ -972,14 +1085,6 @@ function addLegendEntryHitAreas(legendG) {
|
|
|
972
1085
|
d3Selection.select(this).insert("rect", ":first-child").attr("x", bb.x - PAD_X).attr("y", bb.y - PAD_Y).attr("width", bb.width + PAD_X * 2).attr("height", bb.height + PAD_Y * 2).attr("fill", "transparent").attr("data-legend-hit", "");
|
|
973
1086
|
});
|
|
974
1087
|
}
|
|
975
|
-
function eventTagHidden(metadata, hidden) {
|
|
976
|
-
if (hidden.size === 0) return false;
|
|
977
|
-
for (const [k, v] of Object.entries(metadata)) {
|
|
978
|
-
if (hidden.has(`${k.toLowerCase()}:${String(v).toLowerCase()}`))
|
|
979
|
-
return true;
|
|
980
|
-
}
|
|
981
|
-
return false;
|
|
982
|
-
}
|
|
983
1088
|
function applyEvtHidden(container, root) {
|
|
984
1089
|
const hidden = readHidden(container);
|
|
985
1090
|
const isHidden = (el) => {
|
|
@@ -1092,23 +1197,6 @@ function renderEventLineForExport(container, parsed, palette, isDark, exportDims
|
|
|
1092
1197
|
nowDate
|
|
1093
1198
|
);
|
|
1094
1199
|
}
|
|
1095
|
-
function valueToSpineX(v, dated) {
|
|
1096
|
-
if (!dated.length) return null;
|
|
1097
|
-
const first = dated[0];
|
|
1098
|
-
const last = dated[dated.length - 1];
|
|
1099
|
-
if (v <= first.dateValue) return first.x;
|
|
1100
|
-
if (v >= last.dateValue) return last.x;
|
|
1101
|
-
for (let i = 0; i < dated.length - 1; i++) {
|
|
1102
|
-
const a = dated[i];
|
|
1103
|
-
const b = dated[i + 1];
|
|
1104
|
-
if (v >= a.dateValue && v <= b.dateValue) {
|
|
1105
|
-
const span = b.dateValue - a.dateValue;
|
|
1106
|
-
if (span <= 0) return a.x;
|
|
1107
|
-
return a.x + (v - a.dateValue) / span * (b.x - a.x);
|
|
1108
|
-
}
|
|
1109
|
-
}
|
|
1110
|
-
return last.x;
|
|
1111
|
-
}
|
|
1112
1200
|
function axisBreakPaths(cx, cy) {
|
|
1113
1201
|
const HUMPS = 2;
|
|
1114
1202
|
const STEPS = 48;
|
|
@@ -1125,9 +1213,6 @@ function axisBreakPaths(cx, cy) {
|
|
|
1125
1213
|
};
|
|
1126
1214
|
return [wave(cx - BREAK_GAP / 2), wave(cx + BREAK_GAP / 2)];
|
|
1127
1215
|
}
|
|
1128
|
-
function wrapDescription(lines, charsPerLine) {
|
|
1129
|
-
return wrapDescriptionLines([...lines], charsPerLine);
|
|
1130
|
-
}
|
|
1131
1216
|
function renderBody(cardG, lines, bodyColor, palette, startBaseline = CARD_BODY_TOP + DESC_FONT, bulletColors = [], bulletMeta = []) {
|
|
1132
1217
|
let y = startBaseline;
|
|
1133
1218
|
let bulletIdx = 0;
|