@diagrammo/dgmo 0.4.4 → 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 +149 -149
- package/dist/index.cjs +689 -174
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +26 -19
- package/dist/index.d.ts +26 -19
- package/dist/index.js +689 -174
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/chart.ts +5 -2
- package/src/d3.ts +622 -62
- package/src/echarts.ts +13 -12
- package/src/er/parser.ts +88 -3
- package/src/er/renderer.ts +91 -2
- package/src/er/types.ts +3 -0
- package/src/kanban/mutations.ts +1 -1
- package/src/kanban/parser.ts +55 -36
- package/src/sharing.ts +8 -0
package/dist/index.cjs
CHANGED
|
@@ -1379,6 +1379,94 @@ var init_parsing = __esm({
|
|
|
1379
1379
|
}
|
|
1380
1380
|
});
|
|
1381
1381
|
|
|
1382
|
+
// src/utils/tag-groups.ts
|
|
1383
|
+
function isTagBlockHeading(trimmed) {
|
|
1384
|
+
return TAG_BLOCK_RE.test(trimmed) || GROUP_HEADING_RE.test(trimmed);
|
|
1385
|
+
}
|
|
1386
|
+
function resolveTagColor(metadata, tagGroups, activeGroupName, isContainer) {
|
|
1387
|
+
if (!activeGroupName) return void 0;
|
|
1388
|
+
const group = tagGroups.find(
|
|
1389
|
+
(g) => g.name.toLowerCase() === activeGroupName.toLowerCase()
|
|
1390
|
+
);
|
|
1391
|
+
if (!group) return void 0;
|
|
1392
|
+
const metaValue = metadata[group.name.toLowerCase()] ?? (isContainer ? void 0 : group.defaultValue);
|
|
1393
|
+
if (!metaValue) return "#999999";
|
|
1394
|
+
return group.entries.find(
|
|
1395
|
+
(e) => e.value.toLowerCase() === metaValue.toLowerCase()
|
|
1396
|
+
)?.color ?? "#999999";
|
|
1397
|
+
}
|
|
1398
|
+
function validateTagValues(entities, tagGroups, pushWarning, suggestFn) {
|
|
1399
|
+
if (tagGroups.length === 0) return;
|
|
1400
|
+
const groupMap = /* @__PURE__ */ new Map();
|
|
1401
|
+
for (const g of tagGroups) groupMap.set(g.name.toLowerCase(), g);
|
|
1402
|
+
for (const entity of entities) {
|
|
1403
|
+
for (const [key, value] of Object.entries(entity.metadata)) {
|
|
1404
|
+
const group = groupMap.get(key);
|
|
1405
|
+
if (!group) continue;
|
|
1406
|
+
const match = group.entries.some(
|
|
1407
|
+
(e) => e.value.toLowerCase() === value.toLowerCase()
|
|
1408
|
+
);
|
|
1409
|
+
if (!match) {
|
|
1410
|
+
const defined = group.entries.map((e) => e.value);
|
|
1411
|
+
let msg = `Unknown value '${value}' for tag group '${group.name}'`;
|
|
1412
|
+
const hint = suggestFn?.(value, defined);
|
|
1413
|
+
if (hint) {
|
|
1414
|
+
msg += `. ${hint}`;
|
|
1415
|
+
} else {
|
|
1416
|
+
msg += ` \u2014 defined values: ${defined.join(", ")}`;
|
|
1417
|
+
}
|
|
1418
|
+
pushWarning(entity.lineNumber, msg);
|
|
1419
|
+
}
|
|
1420
|
+
}
|
|
1421
|
+
}
|
|
1422
|
+
}
|
|
1423
|
+
function injectDefaultTagMetadata(entities, tagGroups, skip) {
|
|
1424
|
+
const defaults = [];
|
|
1425
|
+
for (const group of tagGroups) {
|
|
1426
|
+
if (group.defaultValue) {
|
|
1427
|
+
defaults.push({ key: group.name.toLowerCase(), value: group.defaultValue });
|
|
1428
|
+
}
|
|
1429
|
+
}
|
|
1430
|
+
if (defaults.length === 0) return;
|
|
1431
|
+
for (const entity of entities) {
|
|
1432
|
+
if (skip?.(entity)) continue;
|
|
1433
|
+
for (const { key, value } of defaults) {
|
|
1434
|
+
if (!(key in entity.metadata)) {
|
|
1435
|
+
entity.metadata[key] = value;
|
|
1436
|
+
}
|
|
1437
|
+
}
|
|
1438
|
+
}
|
|
1439
|
+
}
|
|
1440
|
+
function matchTagBlockHeading(trimmed) {
|
|
1441
|
+
const tagMatch = trimmed.match(TAG_BLOCK_RE);
|
|
1442
|
+
if (tagMatch) {
|
|
1443
|
+
return {
|
|
1444
|
+
name: tagMatch[1].trim(),
|
|
1445
|
+
alias: tagMatch[2] || void 0,
|
|
1446
|
+
colorHint: tagMatch[3] || void 0,
|
|
1447
|
+
deprecated: false
|
|
1448
|
+
};
|
|
1449
|
+
}
|
|
1450
|
+
const groupMatch = trimmed.match(GROUP_HEADING_RE);
|
|
1451
|
+
if (groupMatch) {
|
|
1452
|
+
return {
|
|
1453
|
+
name: groupMatch[1].trim(),
|
|
1454
|
+
alias: groupMatch[2] || void 0,
|
|
1455
|
+
colorHint: groupMatch[3] || void 0,
|
|
1456
|
+
deprecated: true
|
|
1457
|
+
};
|
|
1458
|
+
}
|
|
1459
|
+
return null;
|
|
1460
|
+
}
|
|
1461
|
+
var TAG_BLOCK_RE, GROUP_HEADING_RE;
|
|
1462
|
+
var init_tag_groups = __esm({
|
|
1463
|
+
"src/utils/tag-groups.ts"() {
|
|
1464
|
+
"use strict";
|
|
1465
|
+
TAG_BLOCK_RE = /^tag:\s+(.+?)(?:\s+alias\s+(\w+))?(?:\s*\(([^)]+)\))?\s*$/i;
|
|
1466
|
+
GROUP_HEADING_RE = /^##\s+(.+?)(?:\s+alias\s+(\w+))?(?:\s*\(([^)]+)\))?\s*$/;
|
|
1467
|
+
}
|
|
1468
|
+
});
|
|
1469
|
+
|
|
1382
1470
|
// src/sequence/participant-inference.ts
|
|
1383
1471
|
function inferParticipantType(name) {
|
|
1384
1472
|
for (const rule of PARTICIPANT_RULES) {
|
|
@@ -1708,94 +1796,6 @@ var init_arrows = __esm({
|
|
|
1708
1796
|
}
|
|
1709
1797
|
});
|
|
1710
1798
|
|
|
1711
|
-
// src/utils/tag-groups.ts
|
|
1712
|
-
function isTagBlockHeading(trimmed) {
|
|
1713
|
-
return TAG_BLOCK_RE.test(trimmed) || GROUP_HEADING_RE.test(trimmed);
|
|
1714
|
-
}
|
|
1715
|
-
function resolveTagColor(metadata, tagGroups, activeGroupName, isContainer) {
|
|
1716
|
-
if (!activeGroupName) return void 0;
|
|
1717
|
-
const group = tagGroups.find(
|
|
1718
|
-
(g) => g.name.toLowerCase() === activeGroupName.toLowerCase()
|
|
1719
|
-
);
|
|
1720
|
-
if (!group) return void 0;
|
|
1721
|
-
const metaValue = metadata[group.name.toLowerCase()] ?? (isContainer ? void 0 : group.defaultValue);
|
|
1722
|
-
if (!metaValue) return "#999999";
|
|
1723
|
-
return group.entries.find(
|
|
1724
|
-
(e) => e.value.toLowerCase() === metaValue.toLowerCase()
|
|
1725
|
-
)?.color ?? "#999999";
|
|
1726
|
-
}
|
|
1727
|
-
function validateTagValues(entities, tagGroups, pushWarning, suggestFn) {
|
|
1728
|
-
if (tagGroups.length === 0) return;
|
|
1729
|
-
const groupMap = /* @__PURE__ */ new Map();
|
|
1730
|
-
for (const g of tagGroups) groupMap.set(g.name.toLowerCase(), g);
|
|
1731
|
-
for (const entity of entities) {
|
|
1732
|
-
for (const [key, value] of Object.entries(entity.metadata)) {
|
|
1733
|
-
const group = groupMap.get(key);
|
|
1734
|
-
if (!group) continue;
|
|
1735
|
-
const match = group.entries.some(
|
|
1736
|
-
(e) => e.value.toLowerCase() === value.toLowerCase()
|
|
1737
|
-
);
|
|
1738
|
-
if (!match) {
|
|
1739
|
-
const defined = group.entries.map((e) => e.value);
|
|
1740
|
-
let msg = `Unknown value '${value}' for tag group '${group.name}'`;
|
|
1741
|
-
const hint = suggestFn?.(value, defined);
|
|
1742
|
-
if (hint) {
|
|
1743
|
-
msg += `. ${hint}`;
|
|
1744
|
-
} else {
|
|
1745
|
-
msg += ` \u2014 defined values: ${defined.join(", ")}`;
|
|
1746
|
-
}
|
|
1747
|
-
pushWarning(entity.lineNumber, msg);
|
|
1748
|
-
}
|
|
1749
|
-
}
|
|
1750
|
-
}
|
|
1751
|
-
}
|
|
1752
|
-
function injectDefaultTagMetadata(entities, tagGroups, skip) {
|
|
1753
|
-
const defaults = [];
|
|
1754
|
-
for (const group of tagGroups) {
|
|
1755
|
-
if (group.defaultValue) {
|
|
1756
|
-
defaults.push({ key: group.name.toLowerCase(), value: group.defaultValue });
|
|
1757
|
-
}
|
|
1758
|
-
}
|
|
1759
|
-
if (defaults.length === 0) return;
|
|
1760
|
-
for (const entity of entities) {
|
|
1761
|
-
if (skip?.(entity)) continue;
|
|
1762
|
-
for (const { key, value } of defaults) {
|
|
1763
|
-
if (!(key in entity.metadata)) {
|
|
1764
|
-
entity.metadata[key] = value;
|
|
1765
|
-
}
|
|
1766
|
-
}
|
|
1767
|
-
}
|
|
1768
|
-
}
|
|
1769
|
-
function matchTagBlockHeading(trimmed) {
|
|
1770
|
-
const tagMatch = trimmed.match(TAG_BLOCK_RE);
|
|
1771
|
-
if (tagMatch) {
|
|
1772
|
-
return {
|
|
1773
|
-
name: tagMatch[1].trim(),
|
|
1774
|
-
alias: tagMatch[2] || void 0,
|
|
1775
|
-
colorHint: tagMatch[3] || void 0,
|
|
1776
|
-
deprecated: false
|
|
1777
|
-
};
|
|
1778
|
-
}
|
|
1779
|
-
const groupMatch = trimmed.match(GROUP_HEADING_RE);
|
|
1780
|
-
if (groupMatch) {
|
|
1781
|
-
return {
|
|
1782
|
-
name: groupMatch[1].trim(),
|
|
1783
|
-
alias: groupMatch[2] || void 0,
|
|
1784
|
-
colorHint: groupMatch[3] || void 0,
|
|
1785
|
-
deprecated: true
|
|
1786
|
-
};
|
|
1787
|
-
}
|
|
1788
|
-
return null;
|
|
1789
|
-
}
|
|
1790
|
-
var TAG_BLOCK_RE, GROUP_HEADING_RE;
|
|
1791
|
-
var init_tag_groups = __esm({
|
|
1792
|
-
"src/utils/tag-groups.ts"() {
|
|
1793
|
-
"use strict";
|
|
1794
|
-
TAG_BLOCK_RE = /^tag:\s+(.+?)(?:\s+alias\s+(\w+))?(?:\s*\(([^)]+)\))?\s*$/i;
|
|
1795
|
-
GROUP_HEADING_RE = /^##\s+(.+?)(?:\s+alias\s+(\w+))?(?:\s*\(([^)]+)\))?\s*$/;
|
|
1796
|
-
}
|
|
1797
|
-
});
|
|
1798
|
-
|
|
1799
1799
|
// src/sequence/parser.ts
|
|
1800
1800
|
var parser_exports = {};
|
|
1801
1801
|
__export(parser_exports, {
|
|
@@ -3383,6 +3383,7 @@ function parseERDiagram(content, palette) {
|
|
|
3383
3383
|
options: {},
|
|
3384
3384
|
tables: [],
|
|
3385
3385
|
relationships: [],
|
|
3386
|
+
tagGroups: [],
|
|
3386
3387
|
diagnostics: [],
|
|
3387
3388
|
error: null
|
|
3388
3389
|
};
|
|
@@ -3400,6 +3401,8 @@ function parseERDiagram(content, palette) {
|
|
|
3400
3401
|
const tableMap = /* @__PURE__ */ new Map();
|
|
3401
3402
|
let currentTable = null;
|
|
3402
3403
|
let contentStarted = false;
|
|
3404
|
+
let currentTagGroup = null;
|
|
3405
|
+
const aliasMap = /* @__PURE__ */ new Map();
|
|
3403
3406
|
function getOrCreateTable(name, lineNumber) {
|
|
3404
3407
|
const id = tableId(name);
|
|
3405
3408
|
const existing = tableMap.get(id);
|
|
@@ -3408,6 +3411,7 @@ function parseERDiagram(content, palette) {
|
|
|
3408
3411
|
id,
|
|
3409
3412
|
name,
|
|
3410
3413
|
columns: [],
|
|
3414
|
+
metadata: {},
|
|
3411
3415
|
lineNumber
|
|
3412
3416
|
};
|
|
3413
3417
|
tableMap.set(id, table);
|
|
@@ -3424,6 +3428,50 @@ function parseERDiagram(content, palette) {
|
|
|
3424
3428
|
continue;
|
|
3425
3429
|
}
|
|
3426
3430
|
if (trimmed.startsWith("//")) continue;
|
|
3431
|
+
if (!contentStarted && indent === 0) {
|
|
3432
|
+
const tagBlockMatch = matchTagBlockHeading(trimmed);
|
|
3433
|
+
if (tagBlockMatch) {
|
|
3434
|
+
if (tagBlockMatch.deprecated) {
|
|
3435
|
+
result.diagnostics.push(makeDgmoError(
|
|
3436
|
+
lineNumber,
|
|
3437
|
+
`'## ${tagBlockMatch.name}' is deprecated for tag groups \u2014 use 'tag: ${tagBlockMatch.name}' instead`,
|
|
3438
|
+
"warning"
|
|
3439
|
+
));
|
|
3440
|
+
}
|
|
3441
|
+
currentTagGroup = {
|
|
3442
|
+
name: tagBlockMatch.name,
|
|
3443
|
+
alias: tagBlockMatch.alias,
|
|
3444
|
+
entries: [],
|
|
3445
|
+
lineNumber
|
|
3446
|
+
};
|
|
3447
|
+
if (tagBlockMatch.alias) {
|
|
3448
|
+
aliasMap.set(tagBlockMatch.alias.toLowerCase(), tagBlockMatch.name.toLowerCase());
|
|
3449
|
+
}
|
|
3450
|
+
result.tagGroups.push(currentTagGroup);
|
|
3451
|
+
continue;
|
|
3452
|
+
}
|
|
3453
|
+
}
|
|
3454
|
+
if (currentTagGroup && !contentStarted && indent > 0) {
|
|
3455
|
+
const isDefault = /\bdefault\s*$/.test(trimmed);
|
|
3456
|
+
const entryText = isDefault ? trimmed.replace(/\s+default\s*$/, "").trim() : trimmed;
|
|
3457
|
+
const { label, color } = extractColor(entryText, palette);
|
|
3458
|
+
if (!color) {
|
|
3459
|
+
result.diagnostics.push(makeDgmoError(
|
|
3460
|
+
lineNumber,
|
|
3461
|
+
`Expected 'Value(color)' in tag group '${currentTagGroup.name}'`,
|
|
3462
|
+
"warning"
|
|
3463
|
+
));
|
|
3464
|
+
continue;
|
|
3465
|
+
}
|
|
3466
|
+
if (isDefault) {
|
|
3467
|
+
currentTagGroup.defaultValue = label;
|
|
3468
|
+
}
|
|
3469
|
+
currentTagGroup.entries.push({ value: label, color, lineNumber });
|
|
3470
|
+
continue;
|
|
3471
|
+
}
|
|
3472
|
+
if (currentTagGroup && indent === 0) {
|
|
3473
|
+
currentTagGroup = null;
|
|
3474
|
+
}
|
|
3427
3475
|
if (!contentStarted && indent === 0 && /^[a-z][a-z0-9-]*\s*:/i.test(trimmed)) {
|
|
3428
3476
|
const colonIdx = trimmed.indexOf(":");
|
|
3429
3477
|
const key = trimmed.substring(0, colonIdx).trim().toLowerCase();
|
|
@@ -3505,6 +3553,11 @@ function parseERDiagram(content, palette) {
|
|
|
3505
3553
|
const table = getOrCreateTable(name, lineNumber);
|
|
3506
3554
|
if (color) table.color = color;
|
|
3507
3555
|
table.lineNumber = lineNumber;
|
|
3556
|
+
const pipeStr = tableDecl[3]?.trim();
|
|
3557
|
+
if (pipeStr) {
|
|
3558
|
+
const meta = parsePipeMetadata(["", pipeStr], aliasMap);
|
|
3559
|
+
Object.assign(table.metadata, meta);
|
|
3560
|
+
}
|
|
3508
3561
|
currentTable = table;
|
|
3509
3562
|
continue;
|
|
3510
3563
|
}
|
|
@@ -3514,6 +3567,27 @@ function parseERDiagram(content, palette) {
|
|
|
3514
3567
|
result.diagnostics.push(diag);
|
|
3515
3568
|
result.error = formatDgmoError(diag);
|
|
3516
3569
|
}
|
|
3570
|
+
if (result.tagGroups.length > 0) {
|
|
3571
|
+
const tagEntities = result.tables.map((t) => ({
|
|
3572
|
+
metadata: t.metadata,
|
|
3573
|
+
lineNumber: t.lineNumber
|
|
3574
|
+
}));
|
|
3575
|
+
validateTagValues(
|
|
3576
|
+
tagEntities,
|
|
3577
|
+
result.tagGroups,
|
|
3578
|
+
(line10, msg) => result.diagnostics.push(makeDgmoError(line10, msg, "warning")),
|
|
3579
|
+
suggest
|
|
3580
|
+
);
|
|
3581
|
+
for (const group of result.tagGroups) {
|
|
3582
|
+
if (!group.defaultValue) continue;
|
|
3583
|
+
const key = group.name.toLowerCase();
|
|
3584
|
+
for (const table of result.tables) {
|
|
3585
|
+
if (!table.metadata[key]) {
|
|
3586
|
+
table.metadata[key] = group.defaultValue;
|
|
3587
|
+
}
|
|
3588
|
+
}
|
|
3589
|
+
}
|
|
3590
|
+
}
|
|
3517
3591
|
if (result.tables.length >= 2 && result.relationships.length >= 1 && !result.error) {
|
|
3518
3592
|
const connectedIds = /* @__PURE__ */ new Set();
|
|
3519
3593
|
for (const rel of result.relationships) {
|
|
@@ -3565,7 +3639,8 @@ var init_parser3 = __esm({
|
|
|
3565
3639
|
init_colors();
|
|
3566
3640
|
init_diagnostics();
|
|
3567
3641
|
init_parsing();
|
|
3568
|
-
|
|
3642
|
+
init_tag_groups();
|
|
3643
|
+
TABLE_DECL_RE = /^([a-zA-Z_]\w*)(?:\s*\(([^)]+)\))?(?:\s*\|(.+))?$/;
|
|
3569
3644
|
COLUMN_RE = /^(\w+)(?:\s*:\s*(\w[\w()]*(?:\s*\[\])?))?(?:\s+\[([^\]]+)\])?\s*$/;
|
|
3570
3645
|
INDENT_REL_RE = /^([1*?])-(?:(.+)-)?([1*?])\s+([a-zA-Z_]\w*)\s*$/;
|
|
3571
3646
|
CONSTRAINT_MAP = {
|
|
@@ -3603,7 +3678,10 @@ function parseChart(content, palette) {
|
|
|
3603
3678
|
const trimmed = lines[i].trim();
|
|
3604
3679
|
const lineNumber = i + 1;
|
|
3605
3680
|
if (!trimmed) continue;
|
|
3606
|
-
if (/^#{2,}\s+/.test(trimmed))
|
|
3681
|
+
if (/^#{2,}\s+/.test(trimmed)) {
|
|
3682
|
+
result.diagnostics.push(makeDgmoError(lineNumber, `'${trimmed}' \u2014 ## syntax is no longer supported. Use [Group] containers instead`));
|
|
3683
|
+
continue;
|
|
3684
|
+
}
|
|
3607
3685
|
if (trimmed.startsWith("//")) continue;
|
|
3608
3686
|
const colonIndex = trimmed.indexOf(":");
|
|
3609
3687
|
if (colonIndex === -1) continue;
|
|
@@ -3750,9 +3828,16 @@ function parseEChart(content, palette) {
|
|
|
3750
3828
|
const trimmed = lines[i].trim();
|
|
3751
3829
|
const lineNumber = i + 1;
|
|
3752
3830
|
if (!trimmed) continue;
|
|
3753
|
-
|
|
3754
|
-
|
|
3755
|
-
|
|
3831
|
+
if (/^#{2,}\s+/.test(trimmed)) {
|
|
3832
|
+
const name = trimmed.replace(/^#{2,}\s+/, "").replace(/\s*\([^)]*\)\s*$/, "").trim();
|
|
3833
|
+
result.diagnostics.push(makeDgmoError(lineNumber, `'## ${name}' is no longer supported. Use '[${name}]' instead`));
|
|
3834
|
+
continue;
|
|
3835
|
+
}
|
|
3836
|
+
if (trimmed.startsWith("//")) continue;
|
|
3837
|
+
const categoryMatch = trimmed.match(/^\[(.+?)\](?:\s*\(([^)]+)\))?\s*$/);
|
|
3838
|
+
if (categoryMatch) {
|
|
3839
|
+
const catName = categoryMatch[1].trim();
|
|
3840
|
+
const catColor = categoryMatch[2] ? resolveColor(categoryMatch[2].trim(), palette) : null;
|
|
3756
3841
|
if (catColor) {
|
|
3757
3842
|
if (!result.categoryColors) result.categoryColors = {};
|
|
3758
3843
|
result.categoryColors[catName] = catColor;
|
|
@@ -3760,12 +3845,6 @@ function parseEChart(content, palette) {
|
|
|
3760
3845
|
currentCategory = catName;
|
|
3761
3846
|
continue;
|
|
3762
3847
|
}
|
|
3763
|
-
if (trimmed.startsWith("//")) continue;
|
|
3764
|
-
const categoryMatch = trimmed.match(/^\[(.+)\]$/);
|
|
3765
|
-
if (categoryMatch) {
|
|
3766
|
-
currentCategory = categoryMatch[1].trim();
|
|
3767
|
-
continue;
|
|
3768
|
-
}
|
|
3769
3848
|
const colonIndex = trimmed.indexOf(":");
|
|
3770
3849
|
if (result.type === "sankey" && colonIndex === -1) {
|
|
3771
3850
|
const indent = measureIndent(lines[i]);
|
|
@@ -5397,6 +5476,7 @@ function parseKanban(content, palette) {
|
|
|
5397
5476
|
let currentTagGroup = null;
|
|
5398
5477
|
let currentColumn = null;
|
|
5399
5478
|
let currentCard = null;
|
|
5479
|
+
let cardBaseIndent = 0;
|
|
5400
5480
|
let columnCounter = 0;
|
|
5401
5481
|
let cardCounter = 0;
|
|
5402
5482
|
const aliasMap = /* @__PURE__ */ new Map();
|
|
@@ -5496,7 +5576,14 @@ function parseKanban(content, palette) {
|
|
|
5496
5576
|
}
|
|
5497
5577
|
currentTagGroup = null;
|
|
5498
5578
|
}
|
|
5499
|
-
const
|
|
5579
|
+
const indent = measureIndent(line10);
|
|
5580
|
+
if (LEGACY_COLUMN_RE.test(trimmed)) {
|
|
5581
|
+
const legacyMatch = trimmed.match(LEGACY_COLUMN_RE);
|
|
5582
|
+
const name = legacyMatch[1].replace(/\s*\(.*\)\s*$/, "").trim();
|
|
5583
|
+
warn(lineNumber, `'== ${name} ==' is no longer supported. Use '[${name}]' instead`);
|
|
5584
|
+
continue;
|
|
5585
|
+
}
|
|
5586
|
+
const columnMatch = indent === 0 ? trimmed.match(COLUMN_RE2) : null;
|
|
5500
5587
|
if (columnMatch) {
|
|
5501
5588
|
contentStarted = true;
|
|
5502
5589
|
currentTagGroup = null;
|
|
@@ -5508,16 +5595,20 @@ function parseKanban(content, palette) {
|
|
|
5508
5595
|
}
|
|
5509
5596
|
currentCard = null;
|
|
5510
5597
|
columnCounter++;
|
|
5511
|
-
const
|
|
5512
|
-
const
|
|
5513
|
-
|
|
5514
|
-
|
|
5515
|
-
|
|
5516
|
-
|
|
5598
|
+
const colName = columnMatch[1].trim();
|
|
5599
|
+
const colColor = columnMatch[2] ? resolveColor(columnMatch[2].trim(), palette) : void 0;
|
|
5600
|
+
let wipLimit;
|
|
5601
|
+
const pipeStr = columnMatch[3];
|
|
5602
|
+
if (pipeStr) {
|
|
5603
|
+
const wipMatch = pipeStr.match(/\bwip\s*:\s*(\d+)\b/i);
|
|
5604
|
+
if (wipMatch) {
|
|
5605
|
+
wipLimit = parseInt(wipMatch[1], 10);
|
|
5606
|
+
}
|
|
5607
|
+
}
|
|
5517
5608
|
currentColumn = {
|
|
5518
5609
|
id: `col-${columnCounter}`,
|
|
5519
5610
|
name: colName,
|
|
5520
|
-
wipLimit
|
|
5611
|
+
wipLimit,
|
|
5521
5612
|
color: colColor,
|
|
5522
5613
|
cards: [],
|
|
5523
5614
|
lineNumber
|
|
@@ -5532,24 +5623,25 @@ function parseKanban(content, palette) {
|
|
|
5532
5623
|
warn(lineNumber, "Card line found before any column");
|
|
5533
5624
|
continue;
|
|
5534
5625
|
}
|
|
5535
|
-
|
|
5536
|
-
if (indent > 0 && currentCard) {
|
|
5626
|
+
if (currentCard && indent > cardBaseIndent) {
|
|
5537
5627
|
currentCard.details.push(trimmed);
|
|
5538
5628
|
currentCard.endLineNumber = lineNumber;
|
|
5539
5629
|
continue;
|
|
5540
5630
|
}
|
|
5541
|
-
if (
|
|
5631
|
+
if (indent > 0) {
|
|
5632
|
+
cardCounter++;
|
|
5633
|
+
const card = parseCardLine(
|
|
5634
|
+
trimmed,
|
|
5635
|
+
lineNumber,
|
|
5636
|
+
cardCounter,
|
|
5637
|
+
aliasMap,
|
|
5638
|
+
palette
|
|
5639
|
+
);
|
|
5640
|
+
cardBaseIndent = indent;
|
|
5641
|
+
currentCard = card;
|
|
5642
|
+
currentColumn.cards.push(card);
|
|
5643
|
+
continue;
|
|
5542
5644
|
}
|
|
5543
|
-
cardCounter++;
|
|
5544
|
-
const card = parseCardLine(
|
|
5545
|
-
trimmed,
|
|
5546
|
-
lineNumber,
|
|
5547
|
-
cardCounter,
|
|
5548
|
-
aliasMap,
|
|
5549
|
-
palette
|
|
5550
|
-
);
|
|
5551
|
-
currentCard = card;
|
|
5552
|
-
currentColumn.cards.push(card);
|
|
5553
5645
|
}
|
|
5554
5646
|
if (currentCard) {
|
|
5555
5647
|
}
|
|
@@ -5583,7 +5675,7 @@ function parseKanban(content, palette) {
|
|
|
5583
5675
|
}
|
|
5584
5676
|
}
|
|
5585
5677
|
if (result.columns.length === 0 && !result.error) {
|
|
5586
|
-
return fail(1, "No columns found. Use
|
|
5678
|
+
return fail(1, "No columns found. Use [Column Name] to define columns");
|
|
5587
5679
|
}
|
|
5588
5680
|
return result;
|
|
5589
5681
|
}
|
|
@@ -5620,14 +5712,16 @@ function parseCardLine(trimmed, lineNumber, counter, aliasMap, palette) {
|
|
|
5620
5712
|
color
|
|
5621
5713
|
};
|
|
5622
5714
|
}
|
|
5623
|
-
var COLUMN_RE2;
|
|
5715
|
+
var COLUMN_RE2, LEGACY_COLUMN_RE;
|
|
5624
5716
|
var init_parser5 = __esm({
|
|
5625
5717
|
"src/kanban/parser.ts"() {
|
|
5626
5718
|
"use strict";
|
|
5627
5719
|
init_diagnostics();
|
|
5720
|
+
init_colors();
|
|
5628
5721
|
init_tag_groups();
|
|
5629
5722
|
init_parsing();
|
|
5630
|
-
COLUMN_RE2 =
|
|
5723
|
+
COLUMN_RE2 = /^\[(.+?)\](?:\s*\(([^)]+)\))?\s*(?:\|\s*(.+))?$/;
|
|
5724
|
+
LEGACY_COLUMN_RE = /^==\s+(.+?)\s*(?:\[wip:\s*(\d+)\])?\s*==$/;
|
|
5631
5725
|
}
|
|
5632
5726
|
});
|
|
5633
5727
|
|
|
@@ -9578,7 +9672,7 @@ function computeCardArchive(content, parsed, cardId) {
|
|
|
9578
9672
|
const trimmedEnd = withoutCard.length > 0 && withoutCard[withoutCard.length - 1].trim() === "" ? withoutCard : [...withoutCard, ""];
|
|
9579
9673
|
return [
|
|
9580
9674
|
...trimmedEnd,
|
|
9581
|
-
"
|
|
9675
|
+
"[Archive]",
|
|
9582
9676
|
...cardLines
|
|
9583
9677
|
].join("\n");
|
|
9584
9678
|
}
|
|
@@ -10457,7 +10551,7 @@ function drawCardinality(g, point, prevPoint, cardinality, color, useLabels) {
|
|
|
10457
10551
|
g.append("line").attr("x1", bx + px * spread).attr("y1", by + py * spread).attr("x2", bx - px * spread).attr("y2", by - py * spread).attr("stroke", color).attr("stroke-width", sw);
|
|
10458
10552
|
}
|
|
10459
10553
|
}
|
|
10460
|
-
function renderERDiagram(container, parsed, layout, palette, isDark, onClickItem, exportDims) {
|
|
10554
|
+
function renderERDiagram(container, parsed, layout, palette, isDark, onClickItem, exportDims, activeTagGroup) {
|
|
10461
10555
|
d3Selection5.select(container).selectAll(":not([data-d3-tooltip])").remove();
|
|
10462
10556
|
const width = exportDims?.width ?? container.clientWidth;
|
|
10463
10557
|
const height = exportDims?.height ?? container.clientHeight;
|
|
@@ -10527,8 +10621,16 @@ function renderERDiagram(container, parsed, layout, palette, isDark, onClickItem
|
|
|
10527
10621
|
}
|
|
10528
10622
|
for (let ni = 0; ni < layout.nodes.length; ni++) {
|
|
10529
10623
|
const node = layout.nodes[ni];
|
|
10530
|
-
const
|
|
10624
|
+
const tagColor = resolveTagColor(node.metadata, parsed.tagGroups, activeTagGroup ?? null);
|
|
10625
|
+
const nodeColor2 = node.color ?? tagColor ?? seriesColors2[ni % seriesColors2.length];
|
|
10531
10626
|
const nodeG = contentG.append("g").attr("transform", `translate(${node.x}, ${node.y})`).attr("class", "er-table").attr("data-line-number", String(node.lineNumber)).attr("data-node-id", node.id);
|
|
10627
|
+
if (activeTagGroup) {
|
|
10628
|
+
const tagKey = activeTagGroup.toLowerCase();
|
|
10629
|
+
const tagValue = node.metadata[tagKey];
|
|
10630
|
+
if (tagValue) {
|
|
10631
|
+
nodeG.attr(`data-tag-${tagKey}`, tagValue.toLowerCase());
|
|
10632
|
+
}
|
|
10633
|
+
}
|
|
10532
10634
|
if (onClickItem) {
|
|
10533
10635
|
nodeG.style("cursor", "pointer").on("click", () => {
|
|
10534
10636
|
onClickItem(node.lineNumber);
|
|
@@ -10560,6 +10662,35 @@ function renderERDiagram(container, parsed, layout, palette, isDark, onClickItem
|
|
|
10560
10662
|
}
|
|
10561
10663
|
}
|
|
10562
10664
|
}
|
|
10665
|
+
if (parsed.tagGroups.length > 0) {
|
|
10666
|
+
const LEGEND_Y_PAD = 16;
|
|
10667
|
+
const LEGEND_PILL_H = 22;
|
|
10668
|
+
const LEGEND_PILL_RX = 11;
|
|
10669
|
+
const LEGEND_PILL_PAD9 = 10;
|
|
10670
|
+
const LEGEND_GAP2 = 8;
|
|
10671
|
+
const LEGEND_FONT_SIZE2 = 11;
|
|
10672
|
+
const LEGEND_GROUP_GAP7 = 16;
|
|
10673
|
+
const legendG = svg.append("g").attr("class", "er-tag-legend");
|
|
10674
|
+
let legendX = DIAGRAM_PADDING5;
|
|
10675
|
+
let legendY = height - DIAGRAM_PADDING5;
|
|
10676
|
+
for (const group of parsed.tagGroups) {
|
|
10677
|
+
const groupG = legendG.append("g").attr("data-legend-group", group.name.toLowerCase());
|
|
10678
|
+
const labelText = groupG.append("text").attr("x", legendX).attr("y", legendY + LEGEND_PILL_H / 2).attr("dominant-baseline", "central").attr("fill", palette.textMuted).attr("font-size", LEGEND_FONT_SIZE2).attr("font-family", FONT_FAMILY).text(`${group.name}:`);
|
|
10679
|
+
const labelWidth = (labelText.node()?.getComputedTextLength?.() ?? group.name.length * 7) + 6;
|
|
10680
|
+
legendX += labelWidth;
|
|
10681
|
+
for (const entry of group.entries) {
|
|
10682
|
+
const pillG = groupG.append("g").attr("data-legend-entry", entry.value.toLowerCase()).style("cursor", "pointer");
|
|
10683
|
+
const tmpText = legendG.append("text").attr("font-size", LEGEND_FONT_SIZE2).attr("font-family", FONT_FAMILY).text(entry.value);
|
|
10684
|
+
const textW = tmpText.node()?.getComputedTextLength?.() ?? entry.value.length * 7;
|
|
10685
|
+
tmpText.remove();
|
|
10686
|
+
const pillW = textW + LEGEND_PILL_PAD9 * 2;
|
|
10687
|
+
pillG.append("rect").attr("x", legendX).attr("y", legendY).attr("width", pillW).attr("height", LEGEND_PILL_H).attr("rx", LEGEND_PILL_RX).attr("ry", LEGEND_PILL_RX).attr("fill", mix(entry.color, isDark ? palette.surface : palette.bg, 25)).attr("stroke", entry.color).attr("stroke-width", 1);
|
|
10688
|
+
pillG.append("text").attr("x", legendX + pillW / 2).attr("y", legendY + LEGEND_PILL_H / 2).attr("text-anchor", "middle").attr("dominant-baseline", "central").attr("fill", palette.text).attr("font-size", LEGEND_FONT_SIZE2).attr("font-family", FONT_FAMILY).text(entry.value);
|
|
10689
|
+
legendX += pillW + LEGEND_GAP2;
|
|
10690
|
+
}
|
|
10691
|
+
legendX += LEGEND_GROUP_GAP7;
|
|
10692
|
+
}
|
|
10693
|
+
}
|
|
10563
10694
|
}
|
|
10564
10695
|
function renderERDiagramForExport(content, theme, palette) {
|
|
10565
10696
|
const parsed = parseERDiagram(content, palette);
|
|
@@ -10605,6 +10736,7 @@ var init_renderer5 = __esm({
|
|
|
10605
10736
|
init_fonts();
|
|
10606
10737
|
init_color_utils();
|
|
10607
10738
|
init_palettes();
|
|
10739
|
+
init_tag_groups();
|
|
10608
10740
|
init_parser3();
|
|
10609
10741
|
init_layout4();
|
|
10610
10742
|
DIAGRAM_PADDING5 = 20;
|
|
@@ -18006,6 +18138,7 @@ function parseD3(content, palette) {
|
|
|
18006
18138
|
timelineGroups: [],
|
|
18007
18139
|
timelineEras: [],
|
|
18008
18140
|
timelineMarkers: [],
|
|
18141
|
+
timelineTagGroups: [],
|
|
18009
18142
|
timelineSort: "time",
|
|
18010
18143
|
timelineScale: true,
|
|
18011
18144
|
timelineSwimlanes: false,
|
|
@@ -18040,25 +18173,75 @@ function parseD3(content, palette) {
|
|
|
18040
18173
|
const freeformLines = [];
|
|
18041
18174
|
let currentArcGroup = null;
|
|
18042
18175
|
let currentTimelineGroup = null;
|
|
18176
|
+
let currentTimelineTagGroup = null;
|
|
18177
|
+
const timelineAliasMap = /* @__PURE__ */ new Map();
|
|
18043
18178
|
for (let i = 0; i < lines.length; i++) {
|
|
18044
|
-
const
|
|
18179
|
+
const rawLine = lines[i];
|
|
18180
|
+
const line10 = rawLine.trim();
|
|
18181
|
+
const indent = rawLine.length - rawLine.trimStart().length;
|
|
18045
18182
|
const lineNumber = i + 1;
|
|
18046
18183
|
if (!line10) continue;
|
|
18047
|
-
|
|
18048
|
-
|
|
18184
|
+
if (result.type === "timeline" && indent === 0) {
|
|
18185
|
+
const tagBlockMatch = matchTagBlockHeading(line10);
|
|
18186
|
+
if (tagBlockMatch) {
|
|
18187
|
+
if (tagBlockMatch.deprecated) {
|
|
18188
|
+
result.diagnostics.push(makeDgmoError(
|
|
18189
|
+
lineNumber,
|
|
18190
|
+
`'## ${tagBlockMatch.name}' is deprecated for tag groups \u2014 use 'tag: ${tagBlockMatch.name}' instead`,
|
|
18191
|
+
"warning"
|
|
18192
|
+
));
|
|
18193
|
+
}
|
|
18194
|
+
currentTimelineTagGroup = {
|
|
18195
|
+
name: tagBlockMatch.name,
|
|
18196
|
+
alias: tagBlockMatch.alias,
|
|
18197
|
+
entries: [],
|
|
18198
|
+
lineNumber
|
|
18199
|
+
};
|
|
18200
|
+
if (tagBlockMatch.alias) {
|
|
18201
|
+
timelineAliasMap.set(tagBlockMatch.alias.toLowerCase(), tagBlockMatch.name.toLowerCase());
|
|
18202
|
+
}
|
|
18203
|
+
result.timelineTagGroups.push(currentTimelineTagGroup);
|
|
18204
|
+
continue;
|
|
18205
|
+
}
|
|
18206
|
+
}
|
|
18207
|
+
if (currentTimelineTagGroup && indent > 0) {
|
|
18208
|
+
const trimmedEntry = line10;
|
|
18209
|
+
const isDefault = /\bdefault\s*$/.test(trimmedEntry);
|
|
18210
|
+
const entryText = isDefault ? trimmedEntry.replace(/\s+default\s*$/, "").trim() : trimmedEntry;
|
|
18211
|
+
const { label, color } = extractColor(entryText, palette);
|
|
18212
|
+
if (color) {
|
|
18213
|
+
if (isDefault) currentTimelineTagGroup.defaultValue = label;
|
|
18214
|
+
currentTimelineTagGroup.entries.push({ value: label, color, lineNumber });
|
|
18215
|
+
continue;
|
|
18216
|
+
}
|
|
18217
|
+
}
|
|
18218
|
+
if (currentTimelineTagGroup && indent === 0) {
|
|
18219
|
+
currentTimelineTagGroup = null;
|
|
18220
|
+
}
|
|
18221
|
+
const groupMatch = line10.match(/^\[(.+?)\](?:\s*\(([^)]+)\))?\s*$/);
|
|
18222
|
+
if (groupMatch) {
|
|
18049
18223
|
if (result.type === "arc") {
|
|
18050
|
-
const name =
|
|
18051
|
-
const color =
|
|
18224
|
+
const name = groupMatch[1].trim();
|
|
18225
|
+
const color = groupMatch[2] ? resolveColor(groupMatch[2].trim(), palette) : null;
|
|
18052
18226
|
result.arcNodeGroups.push({ name, nodes: [], color, lineNumber });
|
|
18053
18227
|
currentArcGroup = name;
|
|
18054
18228
|
} else if (result.type === "timeline") {
|
|
18055
|
-
const name =
|
|
18056
|
-
const color =
|
|
18229
|
+
const name = groupMatch[1].trim();
|
|
18230
|
+
const color = groupMatch[2] ? resolveColor(groupMatch[2].trim(), palette) : null;
|
|
18057
18231
|
result.timelineGroups.push({ name, color, lineNumber });
|
|
18058
18232
|
currentTimelineGroup = name;
|
|
18059
18233
|
}
|
|
18060
18234
|
continue;
|
|
18061
18235
|
}
|
|
18236
|
+
if (/^#{2,}\s+/.test(line10) && (result.type === "arc" || result.type === "timeline")) {
|
|
18237
|
+
const name = line10.replace(/^#{2,}\s+/, "").replace(/\s*\([^)]*\)\s*$/, "").trim();
|
|
18238
|
+
result.diagnostics.push(makeDgmoError(lineNumber, `'## ${name}' is no longer supported. Use '[${name}]' instead`, "warning"));
|
|
18239
|
+
continue;
|
|
18240
|
+
}
|
|
18241
|
+
if (indent === 0) {
|
|
18242
|
+
currentArcGroup = null;
|
|
18243
|
+
currentTimelineGroup = null;
|
|
18244
|
+
}
|
|
18062
18245
|
if (line10.startsWith("//")) {
|
|
18063
18246
|
continue;
|
|
18064
18247
|
}
|
|
@@ -18130,11 +18313,14 @@ function parseD3(content, palette) {
|
|
|
18130
18313
|
const amount = parseFloat(durationMatch[2]);
|
|
18131
18314
|
const unit = durationMatch[3];
|
|
18132
18315
|
const endDate = addDurationToDate(startDate, amount, unit);
|
|
18316
|
+
const segments = durationMatch[5].split("|");
|
|
18317
|
+
const metadata = segments.length > 1 ? parsePipeMetadata(["", ...segments.slice(1)], timelineAliasMap) : {};
|
|
18133
18318
|
result.timelineEvents.push({
|
|
18134
18319
|
date: startDate,
|
|
18135
18320
|
endDate,
|
|
18136
|
-
label:
|
|
18321
|
+
label: segments[0].trim(),
|
|
18137
18322
|
group: currentTimelineGroup,
|
|
18323
|
+
metadata,
|
|
18138
18324
|
lineNumber,
|
|
18139
18325
|
uncertain
|
|
18140
18326
|
});
|
|
@@ -18144,11 +18330,14 @@ function parseD3(content, palette) {
|
|
|
18144
18330
|
/^(\d{4}(?:-\d{2})?(?:-\d{2})?)\s*->\s*(\d{4}(?:-\d{2})?(?:-\d{2})?)(\?)?\s*:\s*(.+)$/
|
|
18145
18331
|
);
|
|
18146
18332
|
if (rangeMatch) {
|
|
18333
|
+
const segments = rangeMatch[4].split("|");
|
|
18334
|
+
const metadata = segments.length > 1 ? parsePipeMetadata(["", ...segments.slice(1)], timelineAliasMap) : {};
|
|
18147
18335
|
result.timelineEvents.push({
|
|
18148
18336
|
date: rangeMatch[1],
|
|
18149
18337
|
endDate: rangeMatch[2],
|
|
18150
|
-
label:
|
|
18338
|
+
label: segments[0].trim(),
|
|
18151
18339
|
group: currentTimelineGroup,
|
|
18340
|
+
metadata,
|
|
18152
18341
|
lineNumber,
|
|
18153
18342
|
uncertain: rangeMatch[3] === "?"
|
|
18154
18343
|
});
|
|
@@ -18158,11 +18347,14 @@ function parseD3(content, palette) {
|
|
|
18158
18347
|
/^(\d{4}(?:-\d{2})?(?:-\d{2})?)\s*:\s*(.+)$/
|
|
18159
18348
|
);
|
|
18160
18349
|
if (pointMatch) {
|
|
18350
|
+
const segments = pointMatch[2].split("|");
|
|
18351
|
+
const metadata = segments.length > 1 ? parsePipeMetadata(["", ...segments.slice(1)], timelineAliasMap) : {};
|
|
18161
18352
|
result.timelineEvents.push({
|
|
18162
18353
|
date: pointMatch[1],
|
|
18163
18354
|
endDate: null,
|
|
18164
|
-
label:
|
|
18355
|
+
label: segments[0].trim(),
|
|
18165
18356
|
group: currentTimelineGroup,
|
|
18357
|
+
metadata,
|
|
18166
18358
|
lineNumber
|
|
18167
18359
|
});
|
|
18168
18360
|
continue;
|
|
@@ -18303,9 +18495,18 @@ function parseD3(content, palette) {
|
|
|
18303
18495
|
continue;
|
|
18304
18496
|
}
|
|
18305
18497
|
if (key === "sort") {
|
|
18306
|
-
const v = line10.substring(colonIndex + 1).trim()
|
|
18307
|
-
|
|
18308
|
-
|
|
18498
|
+
const v = line10.substring(colonIndex + 1).trim();
|
|
18499
|
+
const vLower = v.toLowerCase();
|
|
18500
|
+
if (vLower === "time" || vLower === "group") {
|
|
18501
|
+
result.timelineSort = vLower;
|
|
18502
|
+
} else if (vLower === "tag" || vLower.startsWith("tag:")) {
|
|
18503
|
+
result.timelineSort = "tag";
|
|
18504
|
+
if (vLower.startsWith("tag:")) {
|
|
18505
|
+
const groupRef = v.substring(4).trim();
|
|
18506
|
+
if (groupRef) {
|
|
18507
|
+
result.timelineDefaultSwimlaneTG = groupRef;
|
|
18508
|
+
}
|
|
18509
|
+
}
|
|
18309
18510
|
}
|
|
18310
18511
|
continue;
|
|
18311
18512
|
}
|
|
@@ -18425,7 +18626,7 @@ function parseD3(content, palette) {
|
|
|
18425
18626
|
}
|
|
18426
18627
|
if (result.arcNodeGroups.length > 0) {
|
|
18427
18628
|
if (result.arcOrder === "name" || result.arcOrder === "degree") {
|
|
18428
|
-
warn(1, `Cannot use "order: ${result.arcOrder}" with
|
|
18629
|
+
warn(1, `Cannot use "order: ${result.arcOrder}" with [Group] headers. Use "order: group" or remove group headers.`);
|
|
18429
18630
|
result.arcOrder = "group";
|
|
18430
18631
|
}
|
|
18431
18632
|
if (result.arcOrder === "appearance") {
|
|
@@ -18438,6 +18639,42 @@ function parseD3(content, palette) {
|
|
|
18438
18639
|
if (result.timelineEvents.length === 0) {
|
|
18439
18640
|
warn(1, 'No events found. Add events as "YYYY: description" or "YYYY->YYYY: description"');
|
|
18440
18641
|
}
|
|
18642
|
+
if (result.timelineTagGroups.length > 0) {
|
|
18643
|
+
validateTagValues(
|
|
18644
|
+
result.timelineEvents,
|
|
18645
|
+
result.timelineTagGroups,
|
|
18646
|
+
(line10, msg) => result.diagnostics.push(makeDgmoError(line10, msg, "warning")),
|
|
18647
|
+
suggest
|
|
18648
|
+
);
|
|
18649
|
+
for (const group of result.timelineTagGroups) {
|
|
18650
|
+
if (!group.defaultValue) continue;
|
|
18651
|
+
const key = group.name.toLowerCase();
|
|
18652
|
+
for (const event of result.timelineEvents) {
|
|
18653
|
+
if (!event.metadata[key]) {
|
|
18654
|
+
event.metadata[key] = group.defaultValue;
|
|
18655
|
+
}
|
|
18656
|
+
}
|
|
18657
|
+
}
|
|
18658
|
+
}
|
|
18659
|
+
if (result.timelineSort === "tag") {
|
|
18660
|
+
if (result.timelineTagGroups.length === 0) {
|
|
18661
|
+
warn(1, '"sort: tag" requires at least one tag group definition');
|
|
18662
|
+
result.timelineSort = "time";
|
|
18663
|
+
} else if (result.timelineDefaultSwimlaneTG) {
|
|
18664
|
+
const ref = result.timelineDefaultSwimlaneTG.toLowerCase();
|
|
18665
|
+
const match = result.timelineTagGroups.find(
|
|
18666
|
+
(g) => g.name.toLowerCase() === ref || g.alias?.toLowerCase() === ref
|
|
18667
|
+
);
|
|
18668
|
+
if (match) {
|
|
18669
|
+
result.timelineDefaultSwimlaneTG = match.name;
|
|
18670
|
+
} else {
|
|
18671
|
+
warn(1, `"sort: tag:${result.timelineDefaultSwimlaneTG}" \u2014 no tag group matches "${result.timelineDefaultSwimlaneTG}"`);
|
|
18672
|
+
result.timelineDefaultSwimlaneTG = result.timelineTagGroups[0].name;
|
|
18673
|
+
}
|
|
18674
|
+
} else {
|
|
18675
|
+
result.timelineDefaultSwimlaneTG = result.timelineTagGroups[0].name;
|
|
18676
|
+
}
|
|
18677
|
+
}
|
|
18441
18678
|
return result;
|
|
18442
18679
|
}
|
|
18443
18680
|
if (result.type === "venn") {
|
|
@@ -19196,7 +19433,7 @@ function buildEventTooltipHtml(ev) {
|
|
|
19196
19433
|
function buildEraTooltipHtml(era) {
|
|
19197
19434
|
return `<strong>${era.label}</strong><br>${formatDateLabel(era.startDate)} \u2192 ${formatDateLabel(era.endDate)}`;
|
|
19198
19435
|
}
|
|
19199
|
-
function renderTimeline(container, parsed, palette, isDark, onClickItem, exportDims) {
|
|
19436
|
+
function renderTimeline(container, parsed, palette, isDark, onClickItem, exportDims, activeTagGroup, swimlaneTagGroup, onTagStateChange) {
|
|
19200
19437
|
d3Selection12.select(container).selectAll(":not([data-d3-tooltip])").remove();
|
|
19201
19438
|
const {
|
|
19202
19439
|
timelineEvents,
|
|
@@ -19210,6 +19447,9 @@ function renderTimeline(container, parsed, palette, isDark, onClickItem, exportD
|
|
|
19210
19447
|
orientation
|
|
19211
19448
|
} = parsed;
|
|
19212
19449
|
if (timelineEvents.length === 0) return;
|
|
19450
|
+
if (swimlaneTagGroup == null && timelineSort === "tag" && parsed.timelineDefaultSwimlaneTG) {
|
|
19451
|
+
swimlaneTagGroup = parsed.timelineDefaultSwimlaneTG;
|
|
19452
|
+
}
|
|
19213
19453
|
const tooltip = createTooltip(container, palette, isDark);
|
|
19214
19454
|
const width = exportDims?.width ?? container.clientWidth;
|
|
19215
19455
|
const height = exportDims?.height ?? container.clientHeight;
|
|
@@ -19223,7 +19463,49 @@ function renderTimeline(container, parsed, palette, isDark, onClickItem, exportD
|
|
|
19223
19463
|
timelineGroups.forEach((grp, i) => {
|
|
19224
19464
|
groupColorMap.set(grp.name, grp.color ?? colors[i % colors.length]);
|
|
19225
19465
|
});
|
|
19466
|
+
let tagLanes = null;
|
|
19467
|
+
if (swimlaneTagGroup) {
|
|
19468
|
+
const tagKey = swimlaneTagGroup.toLowerCase();
|
|
19469
|
+
const tagGroup = parsed.timelineTagGroups.find(
|
|
19470
|
+
(g) => g.name.toLowerCase() === tagKey
|
|
19471
|
+
);
|
|
19472
|
+
if (tagGroup) {
|
|
19473
|
+
const buckets = /* @__PURE__ */ new Map();
|
|
19474
|
+
const otherEvents = [];
|
|
19475
|
+
for (const ev of timelineEvents) {
|
|
19476
|
+
const val = ev.metadata[tagKey];
|
|
19477
|
+
if (val) {
|
|
19478
|
+
const list = buckets.get(val) ?? [];
|
|
19479
|
+
list.push(ev);
|
|
19480
|
+
buckets.set(val, list);
|
|
19481
|
+
} else {
|
|
19482
|
+
otherEvents.push(ev);
|
|
19483
|
+
}
|
|
19484
|
+
}
|
|
19485
|
+
const laneEntries = [...buckets.entries()].sort((a, b) => {
|
|
19486
|
+
const aMin = Math.min(
|
|
19487
|
+
...a[1].map((e) => parseTimelineDate(e.date))
|
|
19488
|
+
);
|
|
19489
|
+
const bMin = Math.min(
|
|
19490
|
+
...b[1].map((e) => parseTimelineDate(e.date))
|
|
19491
|
+
);
|
|
19492
|
+
return aMin - bMin;
|
|
19493
|
+
});
|
|
19494
|
+
tagLanes = laneEntries.map(([name, events]) => ({ name, events }));
|
|
19495
|
+
if (otherEvents.length > 0) {
|
|
19496
|
+
tagLanes.push({ name: "(Other)", events: otherEvents });
|
|
19497
|
+
}
|
|
19498
|
+
for (const entry of tagGroup.entries) {
|
|
19499
|
+
groupColorMap.set(entry.value, entry.color);
|
|
19500
|
+
}
|
|
19501
|
+
}
|
|
19502
|
+
}
|
|
19503
|
+
const effectiveColorTG = activeTagGroup ?? swimlaneTagGroup ?? null;
|
|
19226
19504
|
function eventColor(ev) {
|
|
19505
|
+
if (effectiveColorTG) {
|
|
19506
|
+
const tagColor = resolveTagColor(ev.metadata, parsed.timelineTagGroups, effectiveColorTG);
|
|
19507
|
+
if (tagColor) return tagColor;
|
|
19508
|
+
}
|
|
19227
19509
|
if (ev.group && groupColorMap.has(ev.group)) {
|
|
19228
19510
|
return groupColorMap.get(ev.group);
|
|
19229
19511
|
}
|
|
@@ -19294,22 +19576,64 @@ function renderTimeline(container, parsed, palette, isDark, onClickItem, exportD
|
|
|
19294
19576
|
}
|
|
19295
19577
|
function fadeReset(g) {
|
|
19296
19578
|
g.selectAll(
|
|
19297
|
-
".tl-event, .tl-legend-item, .tl-lane-header, .tl-marker"
|
|
19579
|
+
".tl-event, .tl-legend-item, .tl-lane-header, .tl-marker, .tl-tag-legend-entry"
|
|
19298
19580
|
).attr("opacity", 1);
|
|
19299
19581
|
g.selectAll(".tl-era").attr("opacity", 1);
|
|
19300
19582
|
}
|
|
19583
|
+
function fadeToTagValue(g, tagKey, tagValue) {
|
|
19584
|
+
const attrName = `data-tag-${tagKey}`;
|
|
19585
|
+
g.selectAll(".tl-event").each(function() {
|
|
19586
|
+
const el = d3Selection12.select(this);
|
|
19587
|
+
const val = el.attr(attrName);
|
|
19588
|
+
el.attr("opacity", val === tagValue ? 1 : FADE_OPACITY);
|
|
19589
|
+
});
|
|
19590
|
+
g.selectAll(".tl-legend-item, .tl-lane-header").attr(
|
|
19591
|
+
"opacity",
|
|
19592
|
+
FADE_OPACITY
|
|
19593
|
+
);
|
|
19594
|
+
g.selectAll(".tl-marker").attr("opacity", FADE_OPACITY);
|
|
19595
|
+
g.selectAll(".tl-tag-legend-entry").each(function() {
|
|
19596
|
+
const el = d3Selection12.select(this);
|
|
19597
|
+
const entryValue = el.attr("data-legend-entry");
|
|
19598
|
+
if (entryValue === "__group__") return;
|
|
19599
|
+
const entryGroup = el.attr("data-tag-group");
|
|
19600
|
+
el.attr("opacity", entryGroup === tagKey && entryValue === tagValue ? 1 : FADE_OPACITY);
|
|
19601
|
+
});
|
|
19602
|
+
}
|
|
19603
|
+
function setTagAttrs(evG, ev) {
|
|
19604
|
+
for (const [key, value] of Object.entries(ev.metadata)) {
|
|
19605
|
+
evG.attr(`data-tag-${key}`, value.toLowerCase());
|
|
19606
|
+
}
|
|
19607
|
+
}
|
|
19608
|
+
const tagLegendReserve = parsed.timelineTagGroups.length > 0 ? 36 : 0;
|
|
19301
19609
|
if (isVertical) {
|
|
19302
|
-
|
|
19303
|
-
|
|
19304
|
-
|
|
19305
|
-
|
|
19306
|
-
)
|
|
19307
|
-
|
|
19610
|
+
const useGroupedVertical = tagLanes != null || timelineSort === "group" && timelineGroups.length > 0;
|
|
19611
|
+
if (useGroupedVertical) {
|
|
19612
|
+
let laneNames;
|
|
19613
|
+
let laneEventsByName;
|
|
19614
|
+
if (tagLanes) {
|
|
19615
|
+
laneNames = tagLanes.map((l) => l.name);
|
|
19616
|
+
laneEventsByName = new Map(tagLanes.map((l) => [l.name, l.events]));
|
|
19617
|
+
} else {
|
|
19618
|
+
const groupNames = timelineGroups.map((gr) => gr.name);
|
|
19619
|
+
const ungroupedEvents = timelineEvents.filter(
|
|
19620
|
+
(ev) => ev.group === null || !groupNames.includes(ev.group)
|
|
19621
|
+
);
|
|
19622
|
+
laneNames = ungroupedEvents.length > 0 ? [...groupNames, "(Other)"] : groupNames;
|
|
19623
|
+
laneEventsByName = new Map(
|
|
19624
|
+
laneNames.map((name) => [
|
|
19625
|
+
name,
|
|
19626
|
+
timelineEvents.filter(
|
|
19627
|
+
(ev) => name === "(Other)" ? ev.group === null || !groupNames.includes(ev.group) : ev.group === name
|
|
19628
|
+
)
|
|
19629
|
+
])
|
|
19630
|
+
);
|
|
19631
|
+
}
|
|
19308
19632
|
const laneCount = laneNames.length;
|
|
19309
19633
|
const scaleMargin = timelineScale ? 40 : 0;
|
|
19310
19634
|
const markerMargin = timelineMarkers.length > 0 ? 30 : 0;
|
|
19311
19635
|
const margin = {
|
|
19312
|
-
top: 104 + markerMargin,
|
|
19636
|
+
top: 104 + markerMargin + tagLegendReserve,
|
|
19313
19637
|
right: 40 + scaleMargin,
|
|
19314
19638
|
bottom: 40,
|
|
19315
19639
|
left: 60 + scaleMargin
|
|
@@ -19359,6 +19683,13 @@ function renderTimeline(container, parsed, palette, isDark, onClickItem, exportD
|
|
|
19359
19683
|
formatDateLabel(latestEndDateStr)
|
|
19360
19684
|
);
|
|
19361
19685
|
}
|
|
19686
|
+
if (timelineSwimlanes || tagLanes) {
|
|
19687
|
+
laneNames.forEach((laneName, laneIdx) => {
|
|
19688
|
+
const laneX = laneIdx * laneWidth;
|
|
19689
|
+
const fillColor = laneIdx % 2 === 0 ? textColor : "transparent";
|
|
19690
|
+
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);
|
|
19691
|
+
});
|
|
19692
|
+
}
|
|
19362
19693
|
laneNames.forEach((laneName, laneIdx) => {
|
|
19363
19694
|
const laneX = laneIdx * laneWidth;
|
|
19364
19695
|
const laneColor = groupColorMap.get(laneName) ?? textColor;
|
|
@@ -19366,9 +19697,7 @@ function renderTimeline(container, parsed, palette, isDark, onClickItem, exportD
|
|
|
19366
19697
|
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));
|
|
19367
19698
|
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);
|
|
19368
19699
|
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");
|
|
19369
|
-
const laneEvents =
|
|
19370
|
-
(ev) => laneName === "(Other)" ? ev.group === null || !groupNames.includes(ev.group) : ev.group === laneName
|
|
19371
|
-
);
|
|
19700
|
+
const laneEvents = laneEventsByName.get(laneName) ?? [];
|
|
19372
19701
|
for (const ev of laneEvents) {
|
|
19373
19702
|
const y = yScale(parseTimelineDate(ev.date));
|
|
19374
19703
|
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(
|
|
@@ -19385,10 +19714,12 @@ function renderTimeline(container, parsed, palette, isDark, onClickItem, exportD
|
|
|
19385
19714
|
}).on("click", () => {
|
|
19386
19715
|
if (onClickItem && ev.lineNumber) onClickItem(ev.lineNumber);
|
|
19387
19716
|
});
|
|
19717
|
+
setTagAttrs(evG, ev);
|
|
19718
|
+
const evColor = eventColor(ev);
|
|
19388
19719
|
if (ev.endDate) {
|
|
19389
19720
|
const y2 = yScale(parseTimelineDate(ev.endDate));
|
|
19390
19721
|
const rectH = Math.max(y2 - y, 4);
|
|
19391
|
-
let fill2 =
|
|
19722
|
+
let fill2 = evColor;
|
|
19392
19723
|
if (ev.uncertain) {
|
|
19393
19724
|
const gradientId = `uncertain-vg-${ev.lineNumber}`;
|
|
19394
19725
|
const defs = svg.select("defs").node() || svg.append("defs").node();
|
|
@@ -19402,7 +19733,7 @@ function renderTimeline(container, parsed, palette, isDark, onClickItem, exportD
|
|
|
19402
19733
|
evG.append("rect").attr("x", laneCenter - 6).attr("y", y).attr("width", 12).attr("height", rectH).attr("rx", 4).attr("fill", fill2);
|
|
19403
19734
|
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);
|
|
19404
19735
|
} else {
|
|
19405
|
-
evG.append("circle").attr("cx", laneCenter).attr("cy", y).attr("r", 4).attr("fill",
|
|
19736
|
+
evG.append("circle").attr("cx", laneCenter).attr("cy", y).attr("r", 4).attr("fill", evColor).attr("stroke", bgColor).attr("stroke-width", 1.5);
|
|
19406
19737
|
evG.append("text").attr("x", laneCenter + 10).attr("y", y).attr("dy", "0.35em").attr("fill", textColor).attr("font-size", "10px").text(ev.label);
|
|
19407
19738
|
}
|
|
19408
19739
|
}
|
|
@@ -19411,7 +19742,7 @@ function renderTimeline(container, parsed, palette, isDark, onClickItem, exportD
|
|
|
19411
19742
|
const scaleMargin = timelineScale ? 40 : 0;
|
|
19412
19743
|
const markerMargin = timelineMarkers.length > 0 ? 30 : 0;
|
|
19413
19744
|
const margin = {
|
|
19414
|
-
top: 104 + markerMargin,
|
|
19745
|
+
top: 104 + markerMargin + tagLegendReserve,
|
|
19415
19746
|
right: 200,
|
|
19416
19747
|
bottom: 40,
|
|
19417
19748
|
left: 60 + scaleMargin
|
|
@@ -19491,6 +19822,7 @@ function renderTimeline(container, parsed, palette, isDark, onClickItem, exportD
|
|
|
19491
19822
|
}).on("click", () => {
|
|
19492
19823
|
if (onClickItem && ev.lineNumber) onClickItem(ev.lineNumber);
|
|
19493
19824
|
});
|
|
19825
|
+
setTagAttrs(evG, ev);
|
|
19494
19826
|
if (ev.endDate) {
|
|
19495
19827
|
const y2 = yScale(parseTimelineDate(ev.endDate));
|
|
19496
19828
|
const rectH = Math.max(y2 - y, 4);
|
|
@@ -19524,18 +19856,24 @@ function renderTimeline(container, parsed, palette, isDark, onClickItem, exportD
|
|
|
19524
19856
|
}
|
|
19525
19857
|
const BAR_H = 22;
|
|
19526
19858
|
const GROUP_GAP = 12;
|
|
19527
|
-
|
|
19528
|
-
|
|
19529
|
-
|
|
19530
|
-
|
|
19531
|
-
|
|
19532
|
-
|
|
19533
|
-
|
|
19534
|
-
|
|
19535
|
-
|
|
19536
|
-
|
|
19537
|
-
)
|
|
19538
|
-
|
|
19859
|
+
const useGroupedHorizontal = tagLanes != null || timelineSort === "group" && timelineGroups.length > 0;
|
|
19860
|
+
if (useGroupedHorizontal) {
|
|
19861
|
+
let lanes;
|
|
19862
|
+
if (tagLanes) {
|
|
19863
|
+
lanes = tagLanes;
|
|
19864
|
+
} else {
|
|
19865
|
+
const groupNames = timelineGroups.map((gr) => gr.name);
|
|
19866
|
+
const ungroupedEvents = timelineEvents.filter(
|
|
19867
|
+
(ev) => ev.group === null || !groupNames.includes(ev.group)
|
|
19868
|
+
);
|
|
19869
|
+
const laneNames = ungroupedEvents.length > 0 ? [...groupNames, "(Other)"] : groupNames;
|
|
19870
|
+
lanes = laneNames.map((name) => ({
|
|
19871
|
+
name,
|
|
19872
|
+
events: timelineEvents.filter(
|
|
19873
|
+
(ev) => name === "(Other)" ? ev.group === null || !groupNames.includes(ev.group) : ev.group === name
|
|
19874
|
+
)
|
|
19875
|
+
}));
|
|
19876
|
+
}
|
|
19539
19877
|
const totalEventRows = lanes.reduce((s, l) => s + l.events.length, 0);
|
|
19540
19878
|
const scaleMargin = timelineScale ? 24 : 0;
|
|
19541
19879
|
const markerMargin = timelineMarkers.length > 0 ? 30 : 0;
|
|
@@ -19543,7 +19881,7 @@ function renderTimeline(container, parsed, palette, isDark, onClickItem, exportD
|
|
|
19543
19881
|
const dynamicLeftMargin = Math.max(120, maxGroupNameLen * 7 + 30);
|
|
19544
19882
|
const baseTopMargin = title ? 50 : 20;
|
|
19545
19883
|
const margin = {
|
|
19546
|
-
top: baseTopMargin + (timelineScale ? 40 : 0) + markerMargin,
|
|
19884
|
+
top: baseTopMargin + (timelineScale ? 40 : 0) + markerMargin + tagLegendReserve,
|
|
19547
19885
|
right: 40,
|
|
19548
19886
|
bottom: 40 + scaleMargin,
|
|
19549
19887
|
left: dynamicLeftMargin
|
|
@@ -19595,7 +19933,7 @@ function renderTimeline(container, parsed, palette, isDark, onClickItem, exportD
|
|
|
19595
19933
|
);
|
|
19596
19934
|
}
|
|
19597
19935
|
let curY = markerMargin;
|
|
19598
|
-
if (timelineSwimlanes) {
|
|
19936
|
+
if (timelineSwimlanes || tagLanes) {
|
|
19599
19937
|
let swimY = markerMargin;
|
|
19600
19938
|
lanes.forEach((lane, idx) => {
|
|
19601
19939
|
const laneSpan = lane.events.length * rowH;
|
|
@@ -19646,12 +19984,14 @@ function renderTimeline(container, parsed, palette, isDark, onClickItem, exportD
|
|
|
19646
19984
|
}).on("click", () => {
|
|
19647
19985
|
if (onClickItem && ev.lineNumber) onClickItem(ev.lineNumber);
|
|
19648
19986
|
});
|
|
19987
|
+
setTagAttrs(evG, ev);
|
|
19988
|
+
const evColor = eventColor(ev);
|
|
19649
19989
|
if (ev.endDate) {
|
|
19650
19990
|
const x2 = xScale(parseTimelineDate(ev.endDate));
|
|
19651
19991
|
const rectW = Math.max(x2 - x, 4);
|
|
19652
19992
|
const estLabelWidth = ev.label.length * 7 + 16;
|
|
19653
19993
|
const labelFitsInside = rectW >= estLabelWidth;
|
|
19654
|
-
let fill2 =
|
|
19994
|
+
let fill2 = evColor;
|
|
19655
19995
|
if (ev.uncertain) {
|
|
19656
19996
|
const gradientId = `uncertain-${ev.lineNumber}`;
|
|
19657
19997
|
const defs = svg.select("defs").node() || svg.append("defs").node();
|
|
@@ -19659,7 +19999,7 @@ function renderTimeline(container, parsed, palette, isDark, onClickItem, exportD
|
|
|
19659
19999
|
{ offset: "0%", opacity: 1 },
|
|
19660
20000
|
{ offset: "80%", opacity: 1 },
|
|
19661
20001
|
{ offset: "100%", opacity: 0 }
|
|
19662
|
-
]).enter().append("stop").attr("offset", (d) => d.offset).attr("stop-color",
|
|
20002
|
+
]).enter().append("stop").attr("offset", (d) => d.offset).attr("stop-color", evColor).attr("stop-opacity", (d) => d.opacity);
|
|
19663
20003
|
fill2 = `url(#${gradientId})`;
|
|
19664
20004
|
}
|
|
19665
20005
|
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);
|
|
@@ -19676,7 +20016,7 @@ function renderTimeline(container, parsed, palette, isDark, onClickItem, exportD
|
|
|
19676
20016
|
const wouldFlipLeft = x > innerWidth * 0.6;
|
|
19677
20017
|
const labelFitsLeft = x - 10 - estLabelWidth > 0;
|
|
19678
20018
|
const flipLeft = wouldFlipLeft && labelFitsLeft;
|
|
19679
|
-
evG.append("circle").attr("cx", x).attr("cy", y).attr("r", 5).attr("fill",
|
|
20019
|
+
evG.append("circle").attr("cx", x).attr("cy", y).attr("r", 5).attr("fill", evColor).attr("stroke", bgColor).attr("stroke-width", 1.5);
|
|
19680
20020
|
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);
|
|
19681
20021
|
}
|
|
19682
20022
|
});
|
|
@@ -19687,7 +20027,7 @@ function renderTimeline(container, parsed, palette, isDark, onClickItem, exportD
|
|
|
19687
20027
|
const scaleMargin = timelineScale ? 24 : 0;
|
|
19688
20028
|
const markerMargin = timelineMarkers.length > 0 ? 30 : 0;
|
|
19689
20029
|
const margin = {
|
|
19690
|
-
top: 104 + (timelineScale ? 40 : 0) + markerMargin,
|
|
20030
|
+
top: 104 + (timelineScale ? 40 : 0) + markerMargin + tagLegendReserve,
|
|
19691
20031
|
right: 40,
|
|
19692
20032
|
bottom: 40 + scaleMargin,
|
|
19693
20033
|
left: 60
|
|
@@ -19783,6 +20123,7 @@ function renderTimeline(container, parsed, palette, isDark, onClickItem, exportD
|
|
|
19783
20123
|
}).on("click", () => {
|
|
19784
20124
|
if (onClickItem && ev.lineNumber) onClickItem(ev.lineNumber);
|
|
19785
20125
|
});
|
|
20126
|
+
setTagAttrs(evG, ev);
|
|
19786
20127
|
if (ev.endDate) {
|
|
19787
20128
|
const x2 = xScale(parseTimelineDate(ev.endDate));
|
|
19788
20129
|
const rectW = Math.max(x2 - x, 4);
|
|
@@ -19818,6 +20159,163 @@ function renderTimeline(container, parsed, palette, isDark, onClickItem, exportD
|
|
|
19818
20159
|
}
|
|
19819
20160
|
});
|
|
19820
20161
|
}
|
|
20162
|
+
if (parsed.timelineTagGroups.length > 0) {
|
|
20163
|
+
const LG_HEIGHT = 28;
|
|
20164
|
+
const LG_PILL_PAD = 16;
|
|
20165
|
+
const LG_PILL_FONT_SIZE = 11;
|
|
20166
|
+
const LG_PILL_FONT_W = LG_PILL_FONT_SIZE * 0.6;
|
|
20167
|
+
const LG_CAPSULE_PAD = 4;
|
|
20168
|
+
const LG_DOT_R = 4;
|
|
20169
|
+
const LG_ENTRY_FONT_SIZE = 10;
|
|
20170
|
+
const LG_ENTRY_FONT_W = LG_ENTRY_FONT_SIZE * 0.6;
|
|
20171
|
+
const LG_ENTRY_DOT_GAP = 4;
|
|
20172
|
+
const LG_ENTRY_TRAIL = 8;
|
|
20173
|
+
const LG_GROUP_GAP = 12;
|
|
20174
|
+
const LG_ICON_W = 20;
|
|
20175
|
+
const mainSvg = d3Selection12.select(container).select("svg");
|
|
20176
|
+
const mainG = mainSvg.select("g");
|
|
20177
|
+
if (!mainSvg.empty() && !mainG.empty()) {
|
|
20178
|
+
let drawSwimlaneIcon2 = function(parent, x, y, isSwimActive) {
|
|
20179
|
+
const iconG = parent.append("g").attr("class", "tl-swimlane-icon").attr("transform", `translate(${x}, ${y})`).style("cursor", "pointer");
|
|
20180
|
+
const barColor = isSwimActive ? palette.primary : palette.textMuted;
|
|
20181
|
+
const barOpacity = isSwimActive ? 1 : 0.35;
|
|
20182
|
+
const bars = [
|
|
20183
|
+
{ y: 0, w: 8 },
|
|
20184
|
+
{ y: 4, w: 12 },
|
|
20185
|
+
{ y: 8, w: 6 }
|
|
20186
|
+
];
|
|
20187
|
+
for (const bar of bars) {
|
|
20188
|
+
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);
|
|
20189
|
+
}
|
|
20190
|
+
return iconG;
|
|
20191
|
+
}, relayout2 = function() {
|
|
20192
|
+
renderTimeline(
|
|
20193
|
+
container,
|
|
20194
|
+
parsed,
|
|
20195
|
+
palette,
|
|
20196
|
+
isDark,
|
|
20197
|
+
onClickItem,
|
|
20198
|
+
exportDims,
|
|
20199
|
+
currentActiveGroup,
|
|
20200
|
+
currentSwimlaneGroup,
|
|
20201
|
+
onTagStateChange
|
|
20202
|
+
);
|
|
20203
|
+
}, drawLegend2 = function() {
|
|
20204
|
+
mainSvg.selectAll(".tl-tag-legend-group").remove();
|
|
20205
|
+
const totalW = legendGroups.reduce((s, lg) => {
|
|
20206
|
+
const isActive = currentActiveGroup != null && lg.group.name.toLowerCase() === currentActiveGroup.toLowerCase();
|
|
20207
|
+
return s + (isActive ? lg.expandedWidth : lg.minifiedWidth);
|
|
20208
|
+
}, 0) + (legendGroups.length - 1) * LG_GROUP_GAP;
|
|
20209
|
+
let cx = (width - totalW) / 2;
|
|
20210
|
+
for (const lg of legendGroups) {
|
|
20211
|
+
const groupKey = lg.group.name.toLowerCase();
|
|
20212
|
+
const isActive = currentActiveGroup != null && currentActiveGroup.toLowerCase() === groupKey;
|
|
20213
|
+
const isSwimActive = currentSwimlaneGroup != null && currentSwimlaneGroup.toLowerCase() === groupKey;
|
|
20214
|
+
const pillLabel = lg.group.name;
|
|
20215
|
+
const pillWidth = pillLabel.length * LG_PILL_FONT_W + LG_PILL_PAD;
|
|
20216
|
+
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", () => {
|
|
20217
|
+
currentActiveGroup = currentActiveGroup === groupKey ? null : groupKey;
|
|
20218
|
+
drawLegend2();
|
|
20219
|
+
recolorEvents2();
|
|
20220
|
+
onTagStateChange?.(currentActiveGroup, currentSwimlaneGroup);
|
|
20221
|
+
});
|
|
20222
|
+
if (isActive) {
|
|
20223
|
+
gEl.append("rect").attr("width", lg.expandedWidth).attr("height", LG_HEIGHT).attr("rx", LG_HEIGHT / 2).attr("fill", groupBg);
|
|
20224
|
+
}
|
|
20225
|
+
const pillXOff = isActive ? LG_CAPSULE_PAD : 0;
|
|
20226
|
+
const pillYOff = isActive ? LG_CAPSULE_PAD : 0;
|
|
20227
|
+
const pillH = LG_HEIGHT - (isActive ? LG_CAPSULE_PAD * 2 : 0);
|
|
20228
|
+
gEl.append("rect").attr("x", pillXOff).attr("y", pillYOff).attr("width", pillWidth).attr("height", pillH).attr("rx", pillH / 2).attr("fill", isActive ? palette.bg : groupBg);
|
|
20229
|
+
if (isActive) {
|
|
20230
|
+
gEl.append("rect").attr("x", pillXOff).attr("y", pillYOff).attr("width", pillWidth).attr("height", pillH).attr("rx", pillH / 2).attr("fill", "none").attr("stroke", mix(palette.textMuted, palette.bg, 50)).attr("stroke-width", 0.75);
|
|
20231
|
+
}
|
|
20232
|
+
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);
|
|
20233
|
+
if (isActive) {
|
|
20234
|
+
const iconX = pillXOff + pillWidth + 5;
|
|
20235
|
+
const iconY = (LG_HEIGHT - 10) / 2;
|
|
20236
|
+
const iconEl = drawSwimlaneIcon2(gEl, iconX, iconY, isSwimActive);
|
|
20237
|
+
iconEl.attr("data-swimlane-toggle", groupKey).on("click", (event) => {
|
|
20238
|
+
event.stopPropagation();
|
|
20239
|
+
currentSwimlaneGroup = currentSwimlaneGroup === groupKey ? null : groupKey;
|
|
20240
|
+
onTagStateChange?.(currentActiveGroup, currentSwimlaneGroup);
|
|
20241
|
+
relayout2();
|
|
20242
|
+
});
|
|
20243
|
+
let entryX = pillXOff + pillWidth + LG_ICON_W + 4;
|
|
20244
|
+
for (const entry of lg.group.entries) {
|
|
20245
|
+
const tagKey = lg.group.name.toLowerCase();
|
|
20246
|
+
const tagVal = entry.value.toLowerCase();
|
|
20247
|
+
const entryG = gEl.append("g").attr("class", "tl-tag-legend-entry").attr("data-tag-group", tagKey).attr("data-legend-entry", tagVal).style("cursor", "pointer").on("mouseenter", (event) => {
|
|
20248
|
+
event.stopPropagation();
|
|
20249
|
+
fadeToTagValue(mainG, tagKey, tagVal);
|
|
20250
|
+
mainSvg.selectAll(".tl-tag-legend-entry").each(function() {
|
|
20251
|
+
const el = d3Selection12.select(this);
|
|
20252
|
+
const ev = el.attr("data-legend-entry");
|
|
20253
|
+
if (ev === "__group__") return;
|
|
20254
|
+
const eg = el.attr("data-tag-group");
|
|
20255
|
+
el.attr("opacity", eg === tagKey && ev === tagVal ? 1 : FADE_OPACITY);
|
|
20256
|
+
});
|
|
20257
|
+
}).on("mouseleave", (event) => {
|
|
20258
|
+
event.stopPropagation();
|
|
20259
|
+
fadeReset(mainG);
|
|
20260
|
+
mainSvg.selectAll(".tl-tag-legend-entry").attr("opacity", 1);
|
|
20261
|
+
}).on("click", (event) => {
|
|
20262
|
+
event.stopPropagation();
|
|
20263
|
+
});
|
|
20264
|
+
entryG.append("circle").attr("cx", entryX + LG_DOT_R).attr("cy", LG_HEIGHT / 2).attr("r", LG_DOT_R).attr("fill", entry.color);
|
|
20265
|
+
const textX = entryX + LG_DOT_R * 2 + LG_ENTRY_DOT_GAP;
|
|
20266
|
+
entryG.append("text").attr("x", textX).attr("y", LG_HEIGHT / 2 + LG_ENTRY_FONT_SIZE / 2 - 1).attr("font-size", LG_ENTRY_FONT_SIZE).attr("font-family", FONT_FAMILY).attr("fill", palette.textMuted).text(entry.value);
|
|
20267
|
+
entryX = textX + entry.value.length * LG_ENTRY_FONT_W + LG_ENTRY_TRAIL;
|
|
20268
|
+
}
|
|
20269
|
+
}
|
|
20270
|
+
cx += (isActive ? lg.expandedWidth : lg.minifiedWidth) + LG_GROUP_GAP;
|
|
20271
|
+
}
|
|
20272
|
+
}, recolorEvents2 = function() {
|
|
20273
|
+
const colorTG = currentActiveGroup ?? swimlaneTagGroup ?? null;
|
|
20274
|
+
mainG.selectAll(".tl-event").each(function() {
|
|
20275
|
+
const el = d3Selection12.select(this);
|
|
20276
|
+
const lineNum = el.attr("data-line-number");
|
|
20277
|
+
const ev = lineNum ? eventByLine.get(lineNum) : void 0;
|
|
20278
|
+
if (!ev) return;
|
|
20279
|
+
let color;
|
|
20280
|
+
if (colorTG) {
|
|
20281
|
+
const tagColor = resolveTagColor(
|
|
20282
|
+
ev.metadata,
|
|
20283
|
+
parsed.timelineTagGroups,
|
|
20284
|
+
colorTG
|
|
20285
|
+
);
|
|
20286
|
+
color = tagColor ?? (ev.group && groupColorMap.has(ev.group) ? groupColorMap.get(ev.group) : textColor);
|
|
20287
|
+
} else {
|
|
20288
|
+
color = ev.group && groupColorMap.has(ev.group) ? groupColorMap.get(ev.group) : textColor;
|
|
20289
|
+
}
|
|
20290
|
+
el.selectAll("rect").attr("fill", color);
|
|
20291
|
+
el.selectAll("circle:not(.tl-event-point-outline)").attr("fill", color);
|
|
20292
|
+
});
|
|
20293
|
+
};
|
|
20294
|
+
var drawSwimlaneIcon = drawSwimlaneIcon2, relayout = relayout2, drawLegend = drawLegend2, recolorEvents = recolorEvents2;
|
|
20295
|
+
const legendY = title ? 50 : 10;
|
|
20296
|
+
const groupBg = isDark ? mix(palette.surface, palette.bg, 50) : mix(palette.surface, palette.bg, 30);
|
|
20297
|
+
const legendGroups = parsed.timelineTagGroups.map((g) => {
|
|
20298
|
+
const pillW = g.name.length * LG_PILL_FONT_W + LG_PILL_PAD;
|
|
20299
|
+
let entryX = LG_CAPSULE_PAD + pillW + LG_ICON_W + 4;
|
|
20300
|
+
for (const entry of g.entries) {
|
|
20301
|
+
const textX = entryX + LG_DOT_R * 2 + LG_ENTRY_DOT_GAP;
|
|
20302
|
+
entryX = textX + entry.value.length * LG_ENTRY_FONT_W + LG_ENTRY_TRAIL;
|
|
20303
|
+
}
|
|
20304
|
+
return {
|
|
20305
|
+
group: g,
|
|
20306
|
+
minifiedWidth: pillW,
|
|
20307
|
+
expandedWidth: entryX + LG_CAPSULE_PAD
|
|
20308
|
+
};
|
|
20309
|
+
});
|
|
20310
|
+
let currentActiveGroup = activeTagGroup ?? null;
|
|
20311
|
+
let currentSwimlaneGroup = swimlaneTagGroup ?? null;
|
|
20312
|
+
const eventByLine = /* @__PURE__ */ new Map();
|
|
20313
|
+
for (const ev of timelineEvents) {
|
|
20314
|
+
eventByLine.set(String(ev.lineNumber), ev);
|
|
20315
|
+
}
|
|
20316
|
+
drawLegend2();
|
|
20317
|
+
}
|
|
20318
|
+
}
|
|
19821
20319
|
}
|
|
19822
20320
|
function getRotateFn(mode) {
|
|
19823
20321
|
if (mode === "mixed") return () => Math.random() > 0.5 ? 0 : 90;
|
|
@@ -20785,7 +21283,16 @@ async function renderD3ForExport(content, theme, palette, orgExportState, option
|
|
|
20785
21283
|
} else if (parsed.type === "arc") {
|
|
20786
21284
|
renderArcDiagram(container, parsed, effectivePalette, isDark, void 0, dims);
|
|
20787
21285
|
} else if (parsed.type === "timeline") {
|
|
20788
|
-
renderTimeline(
|
|
21286
|
+
renderTimeline(
|
|
21287
|
+
container,
|
|
21288
|
+
parsed,
|
|
21289
|
+
effectivePalette,
|
|
21290
|
+
isDark,
|
|
21291
|
+
void 0,
|
|
21292
|
+
dims,
|
|
21293
|
+
orgExportState?.activeTagGroup,
|
|
21294
|
+
orgExportState?.swimlaneTagGroup
|
|
21295
|
+
);
|
|
20789
21296
|
} else if (parsed.type === "venn") {
|
|
20790
21297
|
renderVenn(container, parsed, effectivePalette, isDark, void 0, dims);
|
|
20791
21298
|
} else if (parsed.type === "quadrant") {
|
|
@@ -20808,8 +21315,10 @@ var init_d3 = __esm({
|
|
|
20808
21315
|
init_branding();
|
|
20809
21316
|
init_colors();
|
|
20810
21317
|
init_palettes();
|
|
21318
|
+
init_color_utils();
|
|
20811
21319
|
init_diagnostics();
|
|
20812
21320
|
init_parsing();
|
|
21321
|
+
init_tag_groups();
|
|
20813
21322
|
DEFAULT_CLOUD_OPTIONS = {
|
|
20814
21323
|
rotate: "none",
|
|
20815
21324
|
max: 0,
|
|
@@ -21810,6 +22319,9 @@ function encodeDiagramUrl(dsl, options) {
|
|
|
21810
22319
|
if (options?.viewState?.collapsedGroups?.length) {
|
|
21811
22320
|
hash += `&cg=${encodeURIComponent(options.viewState.collapsedGroups.join(","))}`;
|
|
21812
22321
|
}
|
|
22322
|
+
if (options?.viewState?.swimlaneTagGroup) {
|
|
22323
|
+
hash += `&swim=${encodeURIComponent(options.viewState.swimlaneTagGroup)}`;
|
|
22324
|
+
}
|
|
21813
22325
|
return { url: `${baseUrl}?${hash}#${hash}` };
|
|
21814
22326
|
}
|
|
21815
22327
|
function decodeDiagramUrl(hash) {
|
|
@@ -21833,6 +22345,9 @@ function decodeDiagramUrl(hash) {
|
|
|
21833
22345
|
if (key === "cg" && val) {
|
|
21834
22346
|
viewState.collapsedGroups = val.split(",").filter(Boolean);
|
|
21835
22347
|
}
|
|
22348
|
+
if (key === "swim" && val) {
|
|
22349
|
+
viewState.swimlaneTagGroup = val;
|
|
22350
|
+
}
|
|
21836
22351
|
}
|
|
21837
22352
|
if (payload.startsWith("dgmo=")) {
|
|
21838
22353
|
payload = payload.slice(5);
|