@diagrammo/dgmo 0.5.0 → 0.5.1
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 +146 -146
- package/dist/index.cjs +195 -43
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +5 -2
- package/dist/index.d.ts +5 -2
- package/dist/index.js +195 -43
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/d3.ts +251 -58
- package/src/sharing.ts +8 -0
package/dist/index.d.cts
CHANGED
|
@@ -397,7 +397,7 @@ interface ArcNodeGroup {
|
|
|
397
397
|
color: string | null;
|
|
398
398
|
lineNumber: number;
|
|
399
399
|
}
|
|
400
|
-
type TimelineSort = 'time' | 'group';
|
|
400
|
+
type TimelineSort = 'time' | 'group' | 'tag';
|
|
401
401
|
interface TimelineEvent {
|
|
402
402
|
date: string;
|
|
403
403
|
endDate: string | null;
|
|
@@ -477,6 +477,7 @@ interface ParsedD3 {
|
|
|
477
477
|
timelineMarkers: TimelineMarker[];
|
|
478
478
|
timelineTagGroups: TagGroup[];
|
|
479
479
|
timelineSort: TimelineSort;
|
|
480
|
+
timelineDefaultSwimlaneTG?: string;
|
|
480
481
|
timelineScale: boolean;
|
|
481
482
|
timelineSwimlanes: boolean;
|
|
482
483
|
vennSets: VennSet[];
|
|
@@ -545,7 +546,7 @@ declare function computeTimeTicks(domainMin: number, domainMax: number, scale: d
|
|
|
545
546
|
* Renders a timeline chart into the given container using D3.
|
|
546
547
|
* Supports horizontal (default) and vertical orientation.
|
|
547
548
|
*/
|
|
548
|
-
declare function renderTimeline(container: HTMLDivElement, parsed: ParsedD3, palette: PaletteColors, isDark: boolean, onClickItem?: (lineNumber: number) => void, exportDims?: D3ExportDimensions, activeTagGroup?: string | null): void;
|
|
549
|
+
declare function renderTimeline(container: HTMLDivElement, parsed: ParsedD3, palette: PaletteColors, isDark: boolean, onClickItem?: (lineNumber: number) => void, exportDims?: D3ExportDimensions, activeTagGroup?: string | null, swimlaneTagGroup?: string | null, onTagStateChange?: (activeTagGroup: string | null, swimlaneTagGroup: string | null) => void): void;
|
|
549
550
|
/**
|
|
550
551
|
* Renders a word cloud into the given container using d3-cloud.
|
|
551
552
|
*/
|
|
@@ -564,6 +565,7 @@ declare function renderD3ForExport(content: string, theme: 'light' | 'dark' | 't
|
|
|
564
565
|
collapsedNodes?: Set<string>;
|
|
565
566
|
activeTagGroup?: string | null;
|
|
566
567
|
hiddenAttributes?: Set<string>;
|
|
568
|
+
swimlaneTagGroup?: string | null;
|
|
567
569
|
}, options?: {
|
|
568
570
|
branding?: boolean;
|
|
569
571
|
c4Level?: 'context' | 'containers' | 'components' | 'deployment';
|
|
@@ -1974,6 +1976,7 @@ declare const seriesColors: string[];
|
|
|
1974
1976
|
interface DiagramViewState {
|
|
1975
1977
|
activeTagGroup?: string;
|
|
1976
1978
|
collapsedGroups?: string[];
|
|
1979
|
+
swimlaneTagGroup?: string;
|
|
1977
1980
|
}
|
|
1978
1981
|
interface DecodedDiagramUrl {
|
|
1979
1982
|
dsl: string;
|
package/dist/index.d.ts
CHANGED
|
@@ -397,7 +397,7 @@ interface ArcNodeGroup {
|
|
|
397
397
|
color: string | null;
|
|
398
398
|
lineNumber: number;
|
|
399
399
|
}
|
|
400
|
-
type TimelineSort = 'time' | 'group';
|
|
400
|
+
type TimelineSort = 'time' | 'group' | 'tag';
|
|
401
401
|
interface TimelineEvent {
|
|
402
402
|
date: string;
|
|
403
403
|
endDate: string | null;
|
|
@@ -477,6 +477,7 @@ interface ParsedD3 {
|
|
|
477
477
|
timelineMarkers: TimelineMarker[];
|
|
478
478
|
timelineTagGroups: TagGroup[];
|
|
479
479
|
timelineSort: TimelineSort;
|
|
480
|
+
timelineDefaultSwimlaneTG?: string;
|
|
480
481
|
timelineScale: boolean;
|
|
481
482
|
timelineSwimlanes: boolean;
|
|
482
483
|
vennSets: VennSet[];
|
|
@@ -545,7 +546,7 @@ declare function computeTimeTicks(domainMin: number, domainMax: number, scale: d
|
|
|
545
546
|
* Renders a timeline chart into the given container using D3.
|
|
546
547
|
* Supports horizontal (default) and vertical orientation.
|
|
547
548
|
*/
|
|
548
|
-
declare function renderTimeline(container: HTMLDivElement, parsed: ParsedD3, palette: PaletteColors, isDark: boolean, onClickItem?: (lineNumber: number) => void, exportDims?: D3ExportDimensions, activeTagGroup?: string | null): void;
|
|
549
|
+
declare function renderTimeline(container: HTMLDivElement, parsed: ParsedD3, palette: PaletteColors, isDark: boolean, onClickItem?: (lineNumber: number) => void, exportDims?: D3ExportDimensions, activeTagGroup?: string | null, swimlaneTagGroup?: string | null, onTagStateChange?: (activeTagGroup: string | null, swimlaneTagGroup: string | null) => void): void;
|
|
549
550
|
/**
|
|
550
551
|
* Renders a word cloud into the given container using d3-cloud.
|
|
551
552
|
*/
|
|
@@ -564,6 +565,7 @@ declare function renderD3ForExport(content: string, theme: 'light' | 'dark' | 't
|
|
|
564
565
|
collapsedNodes?: Set<string>;
|
|
565
566
|
activeTagGroup?: string | null;
|
|
566
567
|
hiddenAttributes?: Set<string>;
|
|
568
|
+
swimlaneTagGroup?: string | null;
|
|
567
569
|
}, options?: {
|
|
568
570
|
branding?: boolean;
|
|
569
571
|
c4Level?: 'context' | 'containers' | 'components' | 'deployment';
|
|
@@ -1974,6 +1976,7 @@ declare const seriesColors: string[];
|
|
|
1974
1976
|
interface DiagramViewState {
|
|
1975
1977
|
activeTagGroup?: string;
|
|
1976
1978
|
collapsedGroups?: string[];
|
|
1979
|
+
swimlaneTagGroup?: string;
|
|
1977
1980
|
}
|
|
1978
1981
|
interface DecodedDiagramUrl {
|
|
1979
1982
|
dsl: string;
|
package/dist/index.js
CHANGED
|
@@ -18478,9 +18478,18 @@ function parseD3(content, palette) {
|
|
|
18478
18478
|
continue;
|
|
18479
18479
|
}
|
|
18480
18480
|
if (key === "sort") {
|
|
18481
|
-
const v = line10.substring(colonIndex + 1).trim()
|
|
18482
|
-
|
|
18483
|
-
|
|
18481
|
+
const v = line10.substring(colonIndex + 1).trim();
|
|
18482
|
+
const vLower = v.toLowerCase();
|
|
18483
|
+
if (vLower === "time" || vLower === "group") {
|
|
18484
|
+
result.timelineSort = vLower;
|
|
18485
|
+
} else if (vLower === "tag" || vLower.startsWith("tag:")) {
|
|
18486
|
+
result.timelineSort = "tag";
|
|
18487
|
+
if (vLower.startsWith("tag:")) {
|
|
18488
|
+
const groupRef = v.substring(4).trim();
|
|
18489
|
+
if (groupRef) {
|
|
18490
|
+
result.timelineDefaultSwimlaneTG = groupRef;
|
|
18491
|
+
}
|
|
18492
|
+
}
|
|
18484
18493
|
}
|
|
18485
18494
|
continue;
|
|
18486
18495
|
}
|
|
@@ -18630,6 +18639,25 @@ function parseD3(content, palette) {
|
|
|
18630
18639
|
}
|
|
18631
18640
|
}
|
|
18632
18641
|
}
|
|
18642
|
+
if (result.timelineSort === "tag") {
|
|
18643
|
+
if (result.timelineTagGroups.length === 0) {
|
|
18644
|
+
warn(1, '"sort: tag" requires at least one tag group definition');
|
|
18645
|
+
result.timelineSort = "time";
|
|
18646
|
+
} else if (result.timelineDefaultSwimlaneTG) {
|
|
18647
|
+
const ref = result.timelineDefaultSwimlaneTG.toLowerCase();
|
|
18648
|
+
const match = result.timelineTagGroups.find(
|
|
18649
|
+
(g) => g.name.toLowerCase() === ref || g.alias?.toLowerCase() === ref
|
|
18650
|
+
);
|
|
18651
|
+
if (match) {
|
|
18652
|
+
result.timelineDefaultSwimlaneTG = match.name;
|
|
18653
|
+
} else {
|
|
18654
|
+
warn(1, `"sort: tag:${result.timelineDefaultSwimlaneTG}" \u2014 no tag group matches "${result.timelineDefaultSwimlaneTG}"`);
|
|
18655
|
+
result.timelineDefaultSwimlaneTG = result.timelineTagGroups[0].name;
|
|
18656
|
+
}
|
|
18657
|
+
} else {
|
|
18658
|
+
result.timelineDefaultSwimlaneTG = result.timelineTagGroups[0].name;
|
|
18659
|
+
}
|
|
18660
|
+
}
|
|
18633
18661
|
return result;
|
|
18634
18662
|
}
|
|
18635
18663
|
if (result.type === "venn") {
|
|
@@ -19388,7 +19416,7 @@ function buildEventTooltipHtml(ev) {
|
|
|
19388
19416
|
function buildEraTooltipHtml(era) {
|
|
19389
19417
|
return `<strong>${era.label}</strong><br>${formatDateLabel(era.startDate)} \u2192 ${formatDateLabel(era.endDate)}`;
|
|
19390
19418
|
}
|
|
19391
|
-
function renderTimeline(container, parsed, palette, isDark, onClickItem, exportDims, activeTagGroup) {
|
|
19419
|
+
function renderTimeline(container, parsed, palette, isDark, onClickItem, exportDims, activeTagGroup, swimlaneTagGroup, onTagStateChange) {
|
|
19392
19420
|
d3Selection12.select(container).selectAll(":not([data-d3-tooltip])").remove();
|
|
19393
19421
|
const {
|
|
19394
19422
|
timelineEvents,
|
|
@@ -19402,6 +19430,9 @@ function renderTimeline(container, parsed, palette, isDark, onClickItem, exportD
|
|
|
19402
19430
|
orientation
|
|
19403
19431
|
} = parsed;
|
|
19404
19432
|
if (timelineEvents.length === 0) return;
|
|
19433
|
+
if (swimlaneTagGroup == null && timelineSort === "tag" && parsed.timelineDefaultSwimlaneTG) {
|
|
19434
|
+
swimlaneTagGroup = parsed.timelineDefaultSwimlaneTG;
|
|
19435
|
+
}
|
|
19405
19436
|
const tooltip = createTooltip(container, palette, isDark);
|
|
19406
19437
|
const width = exportDims?.width ?? container.clientWidth;
|
|
19407
19438
|
const height = exportDims?.height ?? container.clientHeight;
|
|
@@ -19415,9 +19446,47 @@ function renderTimeline(container, parsed, palette, isDark, onClickItem, exportD
|
|
|
19415
19446
|
timelineGroups.forEach((grp, i) => {
|
|
19416
19447
|
groupColorMap.set(grp.name, grp.color ?? colors[i % colors.length]);
|
|
19417
19448
|
});
|
|
19449
|
+
let tagLanes = null;
|
|
19450
|
+
if (swimlaneTagGroup) {
|
|
19451
|
+
const tagKey = swimlaneTagGroup.toLowerCase();
|
|
19452
|
+
const tagGroup = parsed.timelineTagGroups.find(
|
|
19453
|
+
(g) => g.name.toLowerCase() === tagKey
|
|
19454
|
+
);
|
|
19455
|
+
if (tagGroup) {
|
|
19456
|
+
const buckets = /* @__PURE__ */ new Map();
|
|
19457
|
+
const otherEvents = [];
|
|
19458
|
+
for (const ev of timelineEvents) {
|
|
19459
|
+
const val = ev.metadata[tagKey];
|
|
19460
|
+
if (val) {
|
|
19461
|
+
const list = buckets.get(val) ?? [];
|
|
19462
|
+
list.push(ev);
|
|
19463
|
+
buckets.set(val, list);
|
|
19464
|
+
} else {
|
|
19465
|
+
otherEvents.push(ev);
|
|
19466
|
+
}
|
|
19467
|
+
}
|
|
19468
|
+
const laneEntries = [...buckets.entries()].sort((a, b) => {
|
|
19469
|
+
const aMin = Math.min(
|
|
19470
|
+
...a[1].map((e) => parseTimelineDate(e.date))
|
|
19471
|
+
);
|
|
19472
|
+
const bMin = Math.min(
|
|
19473
|
+
...b[1].map((e) => parseTimelineDate(e.date))
|
|
19474
|
+
);
|
|
19475
|
+
return aMin - bMin;
|
|
19476
|
+
});
|
|
19477
|
+
tagLanes = laneEntries.map(([name, events]) => ({ name, events }));
|
|
19478
|
+
if (otherEvents.length > 0) {
|
|
19479
|
+
tagLanes.push({ name: "(Other)", events: otherEvents });
|
|
19480
|
+
}
|
|
19481
|
+
for (const entry of tagGroup.entries) {
|
|
19482
|
+
groupColorMap.set(entry.value, entry.color);
|
|
19483
|
+
}
|
|
19484
|
+
}
|
|
19485
|
+
}
|
|
19486
|
+
const effectiveColorTG = activeTagGroup ?? swimlaneTagGroup ?? null;
|
|
19418
19487
|
function eventColor(ev) {
|
|
19419
|
-
if (
|
|
19420
|
-
const tagColor = resolveTagColor(ev.metadata, parsed.timelineTagGroups,
|
|
19488
|
+
if (effectiveColorTG) {
|
|
19489
|
+
const tagColor = resolveTagColor(ev.metadata, parsed.timelineTagGroups, effectiveColorTG);
|
|
19421
19490
|
if (tagColor) return tagColor;
|
|
19422
19491
|
}
|
|
19423
19492
|
if (ev.group && groupColorMap.has(ev.group)) {
|
|
@@ -19521,12 +19590,28 @@ function renderTimeline(container, parsed, palette, isDark, onClickItem, exportD
|
|
|
19521
19590
|
}
|
|
19522
19591
|
const tagLegendReserve = parsed.timelineTagGroups.length > 0 ? 36 : 0;
|
|
19523
19592
|
if (isVertical) {
|
|
19524
|
-
|
|
19525
|
-
|
|
19526
|
-
|
|
19527
|
-
|
|
19528
|
-
)
|
|
19529
|
-
|
|
19593
|
+
const useGroupedVertical = tagLanes != null || timelineSort === "group" && timelineGroups.length > 0;
|
|
19594
|
+
if (useGroupedVertical) {
|
|
19595
|
+
let laneNames;
|
|
19596
|
+
let laneEventsByName;
|
|
19597
|
+
if (tagLanes) {
|
|
19598
|
+
laneNames = tagLanes.map((l) => l.name);
|
|
19599
|
+
laneEventsByName = new Map(tagLanes.map((l) => [l.name, l.events]));
|
|
19600
|
+
} else {
|
|
19601
|
+
const groupNames = timelineGroups.map((gr) => gr.name);
|
|
19602
|
+
const ungroupedEvents = timelineEvents.filter(
|
|
19603
|
+
(ev) => ev.group === null || !groupNames.includes(ev.group)
|
|
19604
|
+
);
|
|
19605
|
+
laneNames = ungroupedEvents.length > 0 ? [...groupNames, "(Other)"] : groupNames;
|
|
19606
|
+
laneEventsByName = new Map(
|
|
19607
|
+
laneNames.map((name) => [
|
|
19608
|
+
name,
|
|
19609
|
+
timelineEvents.filter(
|
|
19610
|
+
(ev) => name === "(Other)" ? ev.group === null || !groupNames.includes(ev.group) : ev.group === name
|
|
19611
|
+
)
|
|
19612
|
+
])
|
|
19613
|
+
);
|
|
19614
|
+
}
|
|
19530
19615
|
const laneCount = laneNames.length;
|
|
19531
19616
|
const scaleMargin = timelineScale ? 40 : 0;
|
|
19532
19617
|
const markerMargin = timelineMarkers.length > 0 ? 30 : 0;
|
|
@@ -19581,6 +19666,13 @@ function renderTimeline(container, parsed, palette, isDark, onClickItem, exportD
|
|
|
19581
19666
|
formatDateLabel(latestEndDateStr)
|
|
19582
19667
|
);
|
|
19583
19668
|
}
|
|
19669
|
+
if (timelineSwimlanes || tagLanes) {
|
|
19670
|
+
laneNames.forEach((laneName, laneIdx) => {
|
|
19671
|
+
const laneX = laneIdx * laneWidth;
|
|
19672
|
+
const fillColor = laneIdx % 2 === 0 ? textColor : "transparent";
|
|
19673
|
+
g.append("rect").attr("class", "tl-swimlane").attr("data-group", laneName).attr("x", laneX).attr("y", 0).attr("width", laneWidth).attr("height", innerHeight).attr("fill", fillColor).attr("opacity", 0.06);
|
|
19674
|
+
});
|
|
19675
|
+
}
|
|
19584
19676
|
laneNames.forEach((laneName, laneIdx) => {
|
|
19585
19677
|
const laneX = laneIdx * laneWidth;
|
|
19586
19678
|
const laneColor = groupColorMap.get(laneName) ?? textColor;
|
|
@@ -19588,9 +19680,7 @@ function renderTimeline(container, parsed, palette, isDark, onClickItem, exportD
|
|
|
19588
19680
|
const headerG = g.append("g").attr("class", "tl-lane-header").attr("data-group", laneName).style("cursor", "pointer").on("mouseenter", () => fadeToGroup(g, laneName)).on("mouseleave", () => fadeReset(g));
|
|
19589
19681
|
headerG.append("text").attr("x", laneCenter).attr("y", -15).attr("text-anchor", "middle").attr("fill", laneColor).attr("font-size", "12px").attr("font-weight", "600").text(laneName);
|
|
19590
19682
|
g.append("line").attr("x1", laneCenter).attr("y1", 0).attr("x2", laneCenter).attr("y2", innerHeight).attr("stroke", mutedColor).attr("stroke-width", 1).attr("stroke-dasharray", "4,4");
|
|
19591
|
-
const laneEvents =
|
|
19592
|
-
(ev) => laneName === "(Other)" ? ev.group === null || !groupNames.includes(ev.group) : ev.group === laneName
|
|
19593
|
-
);
|
|
19683
|
+
const laneEvents = laneEventsByName.get(laneName) ?? [];
|
|
19594
19684
|
for (const ev of laneEvents) {
|
|
19595
19685
|
const y = yScale(parseTimelineDate(ev.date));
|
|
19596
19686
|
const evG = g.append("g").attr("class", "tl-event").attr("data-group", laneName).attr("data-line-number", String(ev.lineNumber)).attr("data-date", String(parseTimelineDate(ev.date))).attr(
|
|
@@ -19608,10 +19698,11 @@ function renderTimeline(container, parsed, palette, isDark, onClickItem, exportD
|
|
|
19608
19698
|
if (onClickItem && ev.lineNumber) onClickItem(ev.lineNumber);
|
|
19609
19699
|
});
|
|
19610
19700
|
setTagAttrs(evG, ev);
|
|
19701
|
+
const evColor = eventColor(ev);
|
|
19611
19702
|
if (ev.endDate) {
|
|
19612
19703
|
const y2 = yScale(parseTimelineDate(ev.endDate));
|
|
19613
19704
|
const rectH = Math.max(y2 - y, 4);
|
|
19614
|
-
let fill2 =
|
|
19705
|
+
let fill2 = evColor;
|
|
19615
19706
|
if (ev.uncertain) {
|
|
19616
19707
|
const gradientId = `uncertain-vg-${ev.lineNumber}`;
|
|
19617
19708
|
const defs = svg.select("defs").node() || svg.append("defs").node();
|
|
@@ -19625,7 +19716,7 @@ function renderTimeline(container, parsed, palette, isDark, onClickItem, exportD
|
|
|
19625
19716
|
evG.append("rect").attr("x", laneCenter - 6).attr("y", y).attr("width", 12).attr("height", rectH).attr("rx", 4).attr("fill", fill2);
|
|
19626
19717
|
evG.append("text").attr("x", laneCenter + 14).attr("y", y + rectH / 2).attr("dy", "0.35em").attr("fill", textColor).attr("font-size", "10px").text(ev.label);
|
|
19627
19718
|
} else {
|
|
19628
|
-
evG.append("circle").attr("cx", laneCenter).attr("cy", y).attr("r", 4).attr("fill",
|
|
19719
|
+
evG.append("circle").attr("cx", laneCenter).attr("cy", y).attr("r", 4).attr("fill", evColor).attr("stroke", bgColor).attr("stroke-width", 1.5);
|
|
19629
19720
|
evG.append("text").attr("x", laneCenter + 10).attr("y", y).attr("dy", "0.35em").attr("fill", textColor).attr("font-size", "10px").text(ev.label);
|
|
19630
19721
|
}
|
|
19631
19722
|
}
|
|
@@ -19748,18 +19839,24 @@ function renderTimeline(container, parsed, palette, isDark, onClickItem, exportD
|
|
|
19748
19839
|
}
|
|
19749
19840
|
const BAR_H = 22;
|
|
19750
19841
|
const GROUP_GAP = 12;
|
|
19751
|
-
|
|
19752
|
-
|
|
19753
|
-
|
|
19754
|
-
|
|
19755
|
-
|
|
19756
|
-
|
|
19757
|
-
|
|
19758
|
-
|
|
19759
|
-
|
|
19760
|
-
|
|
19761
|
-
)
|
|
19762
|
-
|
|
19842
|
+
const useGroupedHorizontal = tagLanes != null || timelineSort === "group" && timelineGroups.length > 0;
|
|
19843
|
+
if (useGroupedHorizontal) {
|
|
19844
|
+
let lanes;
|
|
19845
|
+
if (tagLanes) {
|
|
19846
|
+
lanes = tagLanes;
|
|
19847
|
+
} else {
|
|
19848
|
+
const groupNames = timelineGroups.map((gr) => gr.name);
|
|
19849
|
+
const ungroupedEvents = timelineEvents.filter(
|
|
19850
|
+
(ev) => ev.group === null || !groupNames.includes(ev.group)
|
|
19851
|
+
);
|
|
19852
|
+
const laneNames = ungroupedEvents.length > 0 ? [...groupNames, "(Other)"] : groupNames;
|
|
19853
|
+
lanes = laneNames.map((name) => ({
|
|
19854
|
+
name,
|
|
19855
|
+
events: timelineEvents.filter(
|
|
19856
|
+
(ev) => name === "(Other)" ? ev.group === null || !groupNames.includes(ev.group) : ev.group === name
|
|
19857
|
+
)
|
|
19858
|
+
}));
|
|
19859
|
+
}
|
|
19763
19860
|
const totalEventRows = lanes.reduce((s, l) => s + l.events.length, 0);
|
|
19764
19861
|
const scaleMargin = timelineScale ? 24 : 0;
|
|
19765
19862
|
const markerMargin = timelineMarkers.length > 0 ? 30 : 0;
|
|
@@ -19819,7 +19916,7 @@ function renderTimeline(container, parsed, palette, isDark, onClickItem, exportD
|
|
|
19819
19916
|
);
|
|
19820
19917
|
}
|
|
19821
19918
|
let curY = markerMargin;
|
|
19822
|
-
if (timelineSwimlanes) {
|
|
19919
|
+
if (timelineSwimlanes || tagLanes) {
|
|
19823
19920
|
let swimY = markerMargin;
|
|
19824
19921
|
lanes.forEach((lane, idx) => {
|
|
19825
19922
|
const laneSpan = lane.events.length * rowH;
|
|
@@ -19871,12 +19968,13 @@ function renderTimeline(container, parsed, palette, isDark, onClickItem, exportD
|
|
|
19871
19968
|
if (onClickItem && ev.lineNumber) onClickItem(ev.lineNumber);
|
|
19872
19969
|
});
|
|
19873
19970
|
setTagAttrs(evG, ev);
|
|
19971
|
+
const evColor = eventColor(ev);
|
|
19874
19972
|
if (ev.endDate) {
|
|
19875
19973
|
const x2 = xScale(parseTimelineDate(ev.endDate));
|
|
19876
19974
|
const rectW = Math.max(x2 - x, 4);
|
|
19877
19975
|
const estLabelWidth = ev.label.length * 7 + 16;
|
|
19878
19976
|
const labelFitsInside = rectW >= estLabelWidth;
|
|
19879
|
-
let fill2 =
|
|
19977
|
+
let fill2 = evColor;
|
|
19880
19978
|
if (ev.uncertain) {
|
|
19881
19979
|
const gradientId = `uncertain-${ev.lineNumber}`;
|
|
19882
19980
|
const defs = svg.select("defs").node() || svg.append("defs").node();
|
|
@@ -19884,7 +19982,7 @@ function renderTimeline(container, parsed, palette, isDark, onClickItem, exportD
|
|
|
19884
19982
|
{ offset: "0%", opacity: 1 },
|
|
19885
19983
|
{ offset: "80%", opacity: 1 },
|
|
19886
19984
|
{ offset: "100%", opacity: 0 }
|
|
19887
|
-
]).enter().append("stop").attr("offset", (d) => d.offset).attr("stop-color",
|
|
19985
|
+
]).enter().append("stop").attr("offset", (d) => d.offset).attr("stop-color", evColor).attr("stop-opacity", (d) => d.opacity);
|
|
19888
19986
|
fill2 = `url(#${gradientId})`;
|
|
19889
19987
|
}
|
|
19890
19988
|
evG.append("rect").attr("x", x).attr("y", y - BAR_H / 2).attr("width", rectW).attr("height", BAR_H).attr("rx", 4).attr("fill", fill2);
|
|
@@ -19901,7 +19999,7 @@ function renderTimeline(container, parsed, palette, isDark, onClickItem, exportD
|
|
|
19901
19999
|
const wouldFlipLeft = x > innerWidth * 0.6;
|
|
19902
20000
|
const labelFitsLeft = x - 10 - estLabelWidth > 0;
|
|
19903
20001
|
const flipLeft = wouldFlipLeft && labelFitsLeft;
|
|
19904
|
-
evG.append("circle").attr("cx", x).attr("cy", y).attr("r", 5).attr("fill",
|
|
20002
|
+
evG.append("circle").attr("cx", x).attr("cy", y).attr("r", 5).attr("fill", evColor).attr("stroke", bgColor).attr("stroke-width", 1.5);
|
|
19905
20003
|
evG.append("text").attr("x", flipLeft ? x - 10 : x + 10).attr("y", y).attr("dy", "0.35em").attr("text-anchor", flipLeft ? "end" : "start").attr("fill", textColor).attr("font-size", "12px").text(ev.label);
|
|
19906
20004
|
}
|
|
19907
20005
|
});
|
|
@@ -20056,10 +20154,36 @@ function renderTimeline(container, parsed, palette, isDark, onClickItem, exportD
|
|
|
20056
20154
|
const LG_ENTRY_DOT_GAP = 4;
|
|
20057
20155
|
const LG_ENTRY_TRAIL = 8;
|
|
20058
20156
|
const LG_GROUP_GAP = 12;
|
|
20157
|
+
const LG_ICON_W = 20;
|
|
20059
20158
|
const mainSvg = d3Selection12.select(container).select("svg");
|
|
20060
20159
|
const mainG = mainSvg.select("g");
|
|
20061
20160
|
if (!mainSvg.empty() && !mainG.empty()) {
|
|
20062
|
-
let
|
|
20161
|
+
let drawSwimlaneIcon2 = function(parent, x, y, isSwimActive) {
|
|
20162
|
+
const iconG = parent.append("g").attr("class", "tl-swimlane-icon").attr("transform", `translate(${x}, ${y})`).style("cursor", "pointer");
|
|
20163
|
+
const barColor = isSwimActive ? palette.primary : palette.textMuted;
|
|
20164
|
+
const barOpacity = isSwimActive ? 1 : 0.35;
|
|
20165
|
+
const bars = [
|
|
20166
|
+
{ y: 0, w: 8 },
|
|
20167
|
+
{ y: 4, w: 12 },
|
|
20168
|
+
{ y: 8, w: 6 }
|
|
20169
|
+
];
|
|
20170
|
+
for (const bar of bars) {
|
|
20171
|
+
iconG.append("rect").attr("x", 0).attr("y", bar.y).attr("width", bar.w).attr("height", 2).attr("rx", 1).attr("fill", barColor).attr("opacity", barOpacity);
|
|
20172
|
+
}
|
|
20173
|
+
return iconG;
|
|
20174
|
+
}, relayout2 = function() {
|
|
20175
|
+
renderTimeline(
|
|
20176
|
+
container,
|
|
20177
|
+
parsed,
|
|
20178
|
+
palette,
|
|
20179
|
+
isDark,
|
|
20180
|
+
onClickItem,
|
|
20181
|
+
exportDims,
|
|
20182
|
+
currentActiveGroup,
|
|
20183
|
+
currentSwimlaneGroup,
|
|
20184
|
+
onTagStateChange
|
|
20185
|
+
);
|
|
20186
|
+
}, drawLegend2 = function() {
|
|
20063
20187
|
mainSvg.selectAll(".tl-tag-legend-group").remove();
|
|
20064
20188
|
const totalW = legendGroups.reduce((s, lg) => {
|
|
20065
20189
|
const isActive = currentActiveGroup != null && lg.group.name.toLowerCase() === currentActiveGroup.toLowerCase();
|
|
@@ -20067,14 +20191,16 @@ function renderTimeline(container, parsed, palette, isDark, onClickItem, exportD
|
|
|
20067
20191
|
}, 0) + (legendGroups.length - 1) * LG_GROUP_GAP;
|
|
20068
20192
|
let cx = (width - totalW) / 2;
|
|
20069
20193
|
for (const lg of legendGroups) {
|
|
20070
|
-
const
|
|
20194
|
+
const groupKey = lg.group.name.toLowerCase();
|
|
20195
|
+
const isActive = currentActiveGroup != null && currentActiveGroup.toLowerCase() === groupKey;
|
|
20196
|
+
const isSwimActive = currentSwimlaneGroup != null && currentSwimlaneGroup.toLowerCase() === groupKey;
|
|
20071
20197
|
const pillLabel = lg.group.name;
|
|
20072
20198
|
const pillWidth = pillLabel.length * LG_PILL_FONT_W + LG_PILL_PAD;
|
|
20073
|
-
const gEl = mainSvg.append("g").attr("transform", `translate(${cx}, ${legendY})`).attr("class", "tl-tag-legend-group tl-tag-legend-entry").attr("data-legend-group",
|
|
20074
|
-
const groupKey = lg.group.name.toLowerCase();
|
|
20199
|
+
const gEl = mainSvg.append("g").attr("transform", `translate(${cx}, ${legendY})`).attr("class", "tl-tag-legend-group tl-tag-legend-entry").attr("data-legend-group", groupKey).attr("data-tag-group", groupKey).attr("data-legend-entry", "__group__").style("cursor", "pointer").on("click", () => {
|
|
20075
20200
|
currentActiveGroup = currentActiveGroup === groupKey ? null : groupKey;
|
|
20076
20201
|
drawLegend2();
|
|
20077
20202
|
recolorEvents2();
|
|
20203
|
+
onTagStateChange?.(currentActiveGroup, currentSwimlaneGroup);
|
|
20078
20204
|
});
|
|
20079
20205
|
if (isActive) {
|
|
20080
20206
|
gEl.append("rect").attr("width", lg.expandedWidth).attr("height", LG_HEIGHT).attr("rx", LG_HEIGHT / 2).attr("fill", groupBg);
|
|
@@ -20088,7 +20214,16 @@ function renderTimeline(container, parsed, palette, isDark, onClickItem, exportD
|
|
|
20088
20214
|
}
|
|
20089
20215
|
gEl.append("text").attr("x", pillXOff + pillWidth / 2).attr("y", LG_HEIGHT / 2 + LG_PILL_FONT_SIZE / 2 - 2).attr("font-size", LG_PILL_FONT_SIZE).attr("font-weight", "500").attr("font-family", FONT_FAMILY).attr("fill", isActive ? palette.text : palette.textMuted).attr("text-anchor", "middle").text(pillLabel);
|
|
20090
20216
|
if (isActive) {
|
|
20091
|
-
|
|
20217
|
+
const iconX = pillXOff + pillWidth + 5;
|
|
20218
|
+
const iconY = (LG_HEIGHT - 10) / 2;
|
|
20219
|
+
const iconEl = drawSwimlaneIcon2(gEl, iconX, iconY, isSwimActive);
|
|
20220
|
+
iconEl.attr("data-swimlane-toggle", groupKey).on("click", (event) => {
|
|
20221
|
+
event.stopPropagation();
|
|
20222
|
+
currentSwimlaneGroup = currentSwimlaneGroup === groupKey ? null : groupKey;
|
|
20223
|
+
onTagStateChange?.(currentActiveGroup, currentSwimlaneGroup);
|
|
20224
|
+
relayout2();
|
|
20225
|
+
});
|
|
20226
|
+
let entryX = pillXOff + pillWidth + LG_ICON_W + 4;
|
|
20092
20227
|
for (const entry of lg.group.entries) {
|
|
20093
20228
|
const tagKey = lg.group.name.toLowerCase();
|
|
20094
20229
|
const tagVal = entry.value.toLowerCase();
|
|
@@ -20118,17 +20253,18 @@ function renderTimeline(container, parsed, palette, isDark, onClickItem, exportD
|
|
|
20118
20253
|
cx += (isActive ? lg.expandedWidth : lg.minifiedWidth) + LG_GROUP_GAP;
|
|
20119
20254
|
}
|
|
20120
20255
|
}, recolorEvents2 = function() {
|
|
20256
|
+
const colorTG = currentActiveGroup ?? swimlaneTagGroup ?? null;
|
|
20121
20257
|
mainG.selectAll(".tl-event").each(function() {
|
|
20122
20258
|
const el = d3Selection12.select(this);
|
|
20123
20259
|
const lineNum = el.attr("data-line-number");
|
|
20124
20260
|
const ev = lineNum ? eventByLine.get(lineNum) : void 0;
|
|
20125
20261
|
if (!ev) return;
|
|
20126
20262
|
let color;
|
|
20127
|
-
if (
|
|
20263
|
+
if (colorTG) {
|
|
20128
20264
|
const tagColor = resolveTagColor(
|
|
20129
20265
|
ev.metadata,
|
|
20130
20266
|
parsed.timelineTagGroups,
|
|
20131
|
-
|
|
20267
|
+
colorTG
|
|
20132
20268
|
);
|
|
20133
20269
|
color = tagColor ?? (ev.group && groupColorMap.has(ev.group) ? groupColorMap.get(ev.group) : textColor);
|
|
20134
20270
|
} else {
|
|
@@ -20138,12 +20274,12 @@ function renderTimeline(container, parsed, palette, isDark, onClickItem, exportD
|
|
|
20138
20274
|
el.selectAll("circle:not(.tl-event-point-outline)").attr("fill", color);
|
|
20139
20275
|
});
|
|
20140
20276
|
};
|
|
20141
|
-
var drawLegend = drawLegend2, recolorEvents = recolorEvents2;
|
|
20277
|
+
var drawSwimlaneIcon = drawSwimlaneIcon2, relayout = relayout2, drawLegend = drawLegend2, recolorEvents = recolorEvents2;
|
|
20142
20278
|
const legendY = title ? 50 : 10;
|
|
20143
20279
|
const groupBg = isDark ? mix(palette.surface, palette.bg, 50) : mix(palette.surface, palette.bg, 30);
|
|
20144
20280
|
const legendGroups = parsed.timelineTagGroups.map((g) => {
|
|
20145
20281
|
const pillW = g.name.length * LG_PILL_FONT_W + LG_PILL_PAD;
|
|
20146
|
-
let entryX = LG_CAPSULE_PAD + pillW + 4;
|
|
20282
|
+
let entryX = LG_CAPSULE_PAD + pillW + LG_ICON_W + 4;
|
|
20147
20283
|
for (const entry of g.entries) {
|
|
20148
20284
|
const textX = entryX + LG_DOT_R * 2 + LG_ENTRY_DOT_GAP;
|
|
20149
20285
|
entryX = textX + entry.value.length * LG_ENTRY_FONT_W + LG_ENTRY_TRAIL;
|
|
@@ -20155,6 +20291,7 @@ function renderTimeline(container, parsed, palette, isDark, onClickItem, exportD
|
|
|
20155
20291
|
};
|
|
20156
20292
|
});
|
|
20157
20293
|
let currentActiveGroup = activeTagGroup ?? null;
|
|
20294
|
+
let currentSwimlaneGroup = swimlaneTagGroup ?? null;
|
|
20158
20295
|
const eventByLine = /* @__PURE__ */ new Map();
|
|
20159
20296
|
for (const ev of timelineEvents) {
|
|
20160
20297
|
eventByLine.set(String(ev.lineNumber), ev);
|
|
@@ -21129,7 +21266,16 @@ async function renderD3ForExport(content, theme, palette, orgExportState, option
|
|
|
21129
21266
|
} else if (parsed.type === "arc") {
|
|
21130
21267
|
renderArcDiagram(container, parsed, effectivePalette, isDark, void 0, dims);
|
|
21131
21268
|
} else if (parsed.type === "timeline") {
|
|
21132
|
-
renderTimeline(
|
|
21269
|
+
renderTimeline(
|
|
21270
|
+
container,
|
|
21271
|
+
parsed,
|
|
21272
|
+
effectivePalette,
|
|
21273
|
+
isDark,
|
|
21274
|
+
void 0,
|
|
21275
|
+
dims,
|
|
21276
|
+
orgExportState?.activeTagGroup,
|
|
21277
|
+
orgExportState?.swimlaneTagGroup
|
|
21278
|
+
);
|
|
21133
21279
|
} else if (parsed.type === "venn") {
|
|
21134
21280
|
renderVenn(container, parsed, effectivePalette, isDark, void 0, dims);
|
|
21135
21281
|
} else if (parsed.type === "quadrant") {
|
|
@@ -22012,6 +22158,9 @@ function encodeDiagramUrl(dsl, options) {
|
|
|
22012
22158
|
if (options?.viewState?.collapsedGroups?.length) {
|
|
22013
22159
|
hash += `&cg=${encodeURIComponent(options.viewState.collapsedGroups.join(","))}`;
|
|
22014
22160
|
}
|
|
22161
|
+
if (options?.viewState?.swimlaneTagGroup) {
|
|
22162
|
+
hash += `&swim=${encodeURIComponent(options.viewState.swimlaneTagGroup)}`;
|
|
22163
|
+
}
|
|
22015
22164
|
return { url: `${baseUrl}?${hash}#${hash}` };
|
|
22016
22165
|
}
|
|
22017
22166
|
function decodeDiagramUrl(hash) {
|
|
@@ -22035,6 +22184,9 @@ function decodeDiagramUrl(hash) {
|
|
|
22035
22184
|
if (key === "cg" && val) {
|
|
22036
22185
|
viewState.collapsedGroups = val.split(",").filter(Boolean);
|
|
22037
22186
|
}
|
|
22187
|
+
if (key === "swim" && val) {
|
|
22188
|
+
viewState.swimlaneTagGroup = val;
|
|
22189
|
+
}
|
|
22038
22190
|
}
|
|
22039
22191
|
if (payload.startsWith("dgmo=")) {
|
|
22040
22192
|
payload = payload.slice(5);
|