@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.js
CHANGED
|
@@ -1357,6 +1357,94 @@ var init_parsing = __esm({
|
|
|
1357
1357
|
}
|
|
1358
1358
|
});
|
|
1359
1359
|
|
|
1360
|
+
// src/utils/tag-groups.ts
|
|
1361
|
+
function isTagBlockHeading(trimmed) {
|
|
1362
|
+
return TAG_BLOCK_RE.test(trimmed) || GROUP_HEADING_RE.test(trimmed);
|
|
1363
|
+
}
|
|
1364
|
+
function resolveTagColor(metadata, tagGroups, activeGroupName, isContainer) {
|
|
1365
|
+
if (!activeGroupName) return void 0;
|
|
1366
|
+
const group = tagGroups.find(
|
|
1367
|
+
(g) => g.name.toLowerCase() === activeGroupName.toLowerCase()
|
|
1368
|
+
);
|
|
1369
|
+
if (!group) return void 0;
|
|
1370
|
+
const metaValue = metadata[group.name.toLowerCase()] ?? (isContainer ? void 0 : group.defaultValue);
|
|
1371
|
+
if (!metaValue) return "#999999";
|
|
1372
|
+
return group.entries.find(
|
|
1373
|
+
(e) => e.value.toLowerCase() === metaValue.toLowerCase()
|
|
1374
|
+
)?.color ?? "#999999";
|
|
1375
|
+
}
|
|
1376
|
+
function validateTagValues(entities, tagGroups, pushWarning, suggestFn) {
|
|
1377
|
+
if (tagGroups.length === 0) return;
|
|
1378
|
+
const groupMap = /* @__PURE__ */ new Map();
|
|
1379
|
+
for (const g of tagGroups) groupMap.set(g.name.toLowerCase(), g);
|
|
1380
|
+
for (const entity of entities) {
|
|
1381
|
+
for (const [key, value] of Object.entries(entity.metadata)) {
|
|
1382
|
+
const group = groupMap.get(key);
|
|
1383
|
+
if (!group) continue;
|
|
1384
|
+
const match = group.entries.some(
|
|
1385
|
+
(e) => e.value.toLowerCase() === value.toLowerCase()
|
|
1386
|
+
);
|
|
1387
|
+
if (!match) {
|
|
1388
|
+
const defined = group.entries.map((e) => e.value);
|
|
1389
|
+
let msg = `Unknown value '${value}' for tag group '${group.name}'`;
|
|
1390
|
+
const hint = suggestFn?.(value, defined);
|
|
1391
|
+
if (hint) {
|
|
1392
|
+
msg += `. ${hint}`;
|
|
1393
|
+
} else {
|
|
1394
|
+
msg += ` \u2014 defined values: ${defined.join(", ")}`;
|
|
1395
|
+
}
|
|
1396
|
+
pushWarning(entity.lineNumber, msg);
|
|
1397
|
+
}
|
|
1398
|
+
}
|
|
1399
|
+
}
|
|
1400
|
+
}
|
|
1401
|
+
function injectDefaultTagMetadata(entities, tagGroups, skip) {
|
|
1402
|
+
const defaults = [];
|
|
1403
|
+
for (const group of tagGroups) {
|
|
1404
|
+
if (group.defaultValue) {
|
|
1405
|
+
defaults.push({ key: group.name.toLowerCase(), value: group.defaultValue });
|
|
1406
|
+
}
|
|
1407
|
+
}
|
|
1408
|
+
if (defaults.length === 0) return;
|
|
1409
|
+
for (const entity of entities) {
|
|
1410
|
+
if (skip?.(entity)) continue;
|
|
1411
|
+
for (const { key, value } of defaults) {
|
|
1412
|
+
if (!(key in entity.metadata)) {
|
|
1413
|
+
entity.metadata[key] = value;
|
|
1414
|
+
}
|
|
1415
|
+
}
|
|
1416
|
+
}
|
|
1417
|
+
}
|
|
1418
|
+
function matchTagBlockHeading(trimmed) {
|
|
1419
|
+
const tagMatch = trimmed.match(TAG_BLOCK_RE);
|
|
1420
|
+
if (tagMatch) {
|
|
1421
|
+
return {
|
|
1422
|
+
name: tagMatch[1].trim(),
|
|
1423
|
+
alias: tagMatch[2] || void 0,
|
|
1424
|
+
colorHint: tagMatch[3] || void 0,
|
|
1425
|
+
deprecated: false
|
|
1426
|
+
};
|
|
1427
|
+
}
|
|
1428
|
+
const groupMatch = trimmed.match(GROUP_HEADING_RE);
|
|
1429
|
+
if (groupMatch) {
|
|
1430
|
+
return {
|
|
1431
|
+
name: groupMatch[1].trim(),
|
|
1432
|
+
alias: groupMatch[2] || void 0,
|
|
1433
|
+
colorHint: groupMatch[3] || void 0,
|
|
1434
|
+
deprecated: true
|
|
1435
|
+
};
|
|
1436
|
+
}
|
|
1437
|
+
return null;
|
|
1438
|
+
}
|
|
1439
|
+
var TAG_BLOCK_RE, GROUP_HEADING_RE;
|
|
1440
|
+
var init_tag_groups = __esm({
|
|
1441
|
+
"src/utils/tag-groups.ts"() {
|
|
1442
|
+
"use strict";
|
|
1443
|
+
TAG_BLOCK_RE = /^tag:\s+(.+?)(?:\s+alias\s+(\w+))?(?:\s*\(([^)]+)\))?\s*$/i;
|
|
1444
|
+
GROUP_HEADING_RE = /^##\s+(.+?)(?:\s+alias\s+(\w+))?(?:\s*\(([^)]+)\))?\s*$/;
|
|
1445
|
+
}
|
|
1446
|
+
});
|
|
1447
|
+
|
|
1360
1448
|
// src/sequence/participant-inference.ts
|
|
1361
1449
|
function inferParticipantType(name) {
|
|
1362
1450
|
for (const rule of PARTICIPANT_RULES) {
|
|
@@ -1686,94 +1774,6 @@ var init_arrows = __esm({
|
|
|
1686
1774
|
}
|
|
1687
1775
|
});
|
|
1688
1776
|
|
|
1689
|
-
// src/utils/tag-groups.ts
|
|
1690
|
-
function isTagBlockHeading(trimmed) {
|
|
1691
|
-
return TAG_BLOCK_RE.test(trimmed) || GROUP_HEADING_RE.test(trimmed);
|
|
1692
|
-
}
|
|
1693
|
-
function resolveTagColor(metadata, tagGroups, activeGroupName, isContainer) {
|
|
1694
|
-
if (!activeGroupName) return void 0;
|
|
1695
|
-
const group = tagGroups.find(
|
|
1696
|
-
(g) => g.name.toLowerCase() === activeGroupName.toLowerCase()
|
|
1697
|
-
);
|
|
1698
|
-
if (!group) return void 0;
|
|
1699
|
-
const metaValue = metadata[group.name.toLowerCase()] ?? (isContainer ? void 0 : group.defaultValue);
|
|
1700
|
-
if (!metaValue) return "#999999";
|
|
1701
|
-
return group.entries.find(
|
|
1702
|
-
(e) => e.value.toLowerCase() === metaValue.toLowerCase()
|
|
1703
|
-
)?.color ?? "#999999";
|
|
1704
|
-
}
|
|
1705
|
-
function validateTagValues(entities, tagGroups, pushWarning, suggestFn) {
|
|
1706
|
-
if (tagGroups.length === 0) return;
|
|
1707
|
-
const groupMap = /* @__PURE__ */ new Map();
|
|
1708
|
-
for (const g of tagGroups) groupMap.set(g.name.toLowerCase(), g);
|
|
1709
|
-
for (const entity of entities) {
|
|
1710
|
-
for (const [key, value] of Object.entries(entity.metadata)) {
|
|
1711
|
-
const group = groupMap.get(key);
|
|
1712
|
-
if (!group) continue;
|
|
1713
|
-
const match = group.entries.some(
|
|
1714
|
-
(e) => e.value.toLowerCase() === value.toLowerCase()
|
|
1715
|
-
);
|
|
1716
|
-
if (!match) {
|
|
1717
|
-
const defined = group.entries.map((e) => e.value);
|
|
1718
|
-
let msg = `Unknown value '${value}' for tag group '${group.name}'`;
|
|
1719
|
-
const hint = suggestFn?.(value, defined);
|
|
1720
|
-
if (hint) {
|
|
1721
|
-
msg += `. ${hint}`;
|
|
1722
|
-
} else {
|
|
1723
|
-
msg += ` \u2014 defined values: ${defined.join(", ")}`;
|
|
1724
|
-
}
|
|
1725
|
-
pushWarning(entity.lineNumber, msg);
|
|
1726
|
-
}
|
|
1727
|
-
}
|
|
1728
|
-
}
|
|
1729
|
-
}
|
|
1730
|
-
function injectDefaultTagMetadata(entities, tagGroups, skip) {
|
|
1731
|
-
const defaults = [];
|
|
1732
|
-
for (const group of tagGroups) {
|
|
1733
|
-
if (group.defaultValue) {
|
|
1734
|
-
defaults.push({ key: group.name.toLowerCase(), value: group.defaultValue });
|
|
1735
|
-
}
|
|
1736
|
-
}
|
|
1737
|
-
if (defaults.length === 0) return;
|
|
1738
|
-
for (const entity of entities) {
|
|
1739
|
-
if (skip?.(entity)) continue;
|
|
1740
|
-
for (const { key, value } of defaults) {
|
|
1741
|
-
if (!(key in entity.metadata)) {
|
|
1742
|
-
entity.metadata[key] = value;
|
|
1743
|
-
}
|
|
1744
|
-
}
|
|
1745
|
-
}
|
|
1746
|
-
}
|
|
1747
|
-
function matchTagBlockHeading(trimmed) {
|
|
1748
|
-
const tagMatch = trimmed.match(TAG_BLOCK_RE);
|
|
1749
|
-
if (tagMatch) {
|
|
1750
|
-
return {
|
|
1751
|
-
name: tagMatch[1].trim(),
|
|
1752
|
-
alias: tagMatch[2] || void 0,
|
|
1753
|
-
colorHint: tagMatch[3] || void 0,
|
|
1754
|
-
deprecated: false
|
|
1755
|
-
};
|
|
1756
|
-
}
|
|
1757
|
-
const groupMatch = trimmed.match(GROUP_HEADING_RE);
|
|
1758
|
-
if (groupMatch) {
|
|
1759
|
-
return {
|
|
1760
|
-
name: groupMatch[1].trim(),
|
|
1761
|
-
alias: groupMatch[2] || void 0,
|
|
1762
|
-
colorHint: groupMatch[3] || void 0,
|
|
1763
|
-
deprecated: true
|
|
1764
|
-
};
|
|
1765
|
-
}
|
|
1766
|
-
return null;
|
|
1767
|
-
}
|
|
1768
|
-
var TAG_BLOCK_RE, GROUP_HEADING_RE;
|
|
1769
|
-
var init_tag_groups = __esm({
|
|
1770
|
-
"src/utils/tag-groups.ts"() {
|
|
1771
|
-
"use strict";
|
|
1772
|
-
TAG_BLOCK_RE = /^tag:\s+(.+?)(?:\s+alias\s+(\w+))?(?:\s*\(([^)]+)\))?\s*$/i;
|
|
1773
|
-
GROUP_HEADING_RE = /^##\s+(.+?)(?:\s+alias\s+(\w+))?(?:\s*\(([^)]+)\))?\s*$/;
|
|
1774
|
-
}
|
|
1775
|
-
});
|
|
1776
|
-
|
|
1777
1777
|
// src/sequence/parser.ts
|
|
1778
1778
|
var parser_exports = {};
|
|
1779
1779
|
__export(parser_exports, {
|
|
@@ -3361,6 +3361,7 @@ function parseERDiagram(content, palette) {
|
|
|
3361
3361
|
options: {},
|
|
3362
3362
|
tables: [],
|
|
3363
3363
|
relationships: [],
|
|
3364
|
+
tagGroups: [],
|
|
3364
3365
|
diagnostics: [],
|
|
3365
3366
|
error: null
|
|
3366
3367
|
};
|
|
@@ -3378,6 +3379,8 @@ function parseERDiagram(content, palette) {
|
|
|
3378
3379
|
const tableMap = /* @__PURE__ */ new Map();
|
|
3379
3380
|
let currentTable = null;
|
|
3380
3381
|
let contentStarted = false;
|
|
3382
|
+
let currentTagGroup = null;
|
|
3383
|
+
const aliasMap = /* @__PURE__ */ new Map();
|
|
3381
3384
|
function getOrCreateTable(name, lineNumber) {
|
|
3382
3385
|
const id = tableId(name);
|
|
3383
3386
|
const existing = tableMap.get(id);
|
|
@@ -3386,6 +3389,7 @@ function parseERDiagram(content, palette) {
|
|
|
3386
3389
|
id,
|
|
3387
3390
|
name,
|
|
3388
3391
|
columns: [],
|
|
3392
|
+
metadata: {},
|
|
3389
3393
|
lineNumber
|
|
3390
3394
|
};
|
|
3391
3395
|
tableMap.set(id, table);
|
|
@@ -3402,6 +3406,50 @@ function parseERDiagram(content, palette) {
|
|
|
3402
3406
|
continue;
|
|
3403
3407
|
}
|
|
3404
3408
|
if (trimmed.startsWith("//")) continue;
|
|
3409
|
+
if (!contentStarted && indent === 0) {
|
|
3410
|
+
const tagBlockMatch = matchTagBlockHeading(trimmed);
|
|
3411
|
+
if (tagBlockMatch) {
|
|
3412
|
+
if (tagBlockMatch.deprecated) {
|
|
3413
|
+
result.diagnostics.push(makeDgmoError(
|
|
3414
|
+
lineNumber,
|
|
3415
|
+
`'## ${tagBlockMatch.name}' is deprecated for tag groups \u2014 use 'tag: ${tagBlockMatch.name}' instead`,
|
|
3416
|
+
"warning"
|
|
3417
|
+
));
|
|
3418
|
+
}
|
|
3419
|
+
currentTagGroup = {
|
|
3420
|
+
name: tagBlockMatch.name,
|
|
3421
|
+
alias: tagBlockMatch.alias,
|
|
3422
|
+
entries: [],
|
|
3423
|
+
lineNumber
|
|
3424
|
+
};
|
|
3425
|
+
if (tagBlockMatch.alias) {
|
|
3426
|
+
aliasMap.set(tagBlockMatch.alias.toLowerCase(), tagBlockMatch.name.toLowerCase());
|
|
3427
|
+
}
|
|
3428
|
+
result.tagGroups.push(currentTagGroup);
|
|
3429
|
+
continue;
|
|
3430
|
+
}
|
|
3431
|
+
}
|
|
3432
|
+
if (currentTagGroup && !contentStarted && indent > 0) {
|
|
3433
|
+
const isDefault = /\bdefault\s*$/.test(trimmed);
|
|
3434
|
+
const entryText = isDefault ? trimmed.replace(/\s+default\s*$/, "").trim() : trimmed;
|
|
3435
|
+
const { label, color } = extractColor(entryText, palette);
|
|
3436
|
+
if (!color) {
|
|
3437
|
+
result.diagnostics.push(makeDgmoError(
|
|
3438
|
+
lineNumber,
|
|
3439
|
+
`Expected 'Value(color)' in tag group '${currentTagGroup.name}'`,
|
|
3440
|
+
"warning"
|
|
3441
|
+
));
|
|
3442
|
+
continue;
|
|
3443
|
+
}
|
|
3444
|
+
if (isDefault) {
|
|
3445
|
+
currentTagGroup.defaultValue = label;
|
|
3446
|
+
}
|
|
3447
|
+
currentTagGroup.entries.push({ value: label, color, lineNumber });
|
|
3448
|
+
continue;
|
|
3449
|
+
}
|
|
3450
|
+
if (currentTagGroup && indent === 0) {
|
|
3451
|
+
currentTagGroup = null;
|
|
3452
|
+
}
|
|
3405
3453
|
if (!contentStarted && indent === 0 && /^[a-z][a-z0-9-]*\s*:/i.test(trimmed)) {
|
|
3406
3454
|
const colonIdx = trimmed.indexOf(":");
|
|
3407
3455
|
const key = trimmed.substring(0, colonIdx).trim().toLowerCase();
|
|
@@ -3483,6 +3531,11 @@ function parseERDiagram(content, palette) {
|
|
|
3483
3531
|
const table = getOrCreateTable(name, lineNumber);
|
|
3484
3532
|
if (color) table.color = color;
|
|
3485
3533
|
table.lineNumber = lineNumber;
|
|
3534
|
+
const pipeStr = tableDecl[3]?.trim();
|
|
3535
|
+
if (pipeStr) {
|
|
3536
|
+
const meta = parsePipeMetadata(["", pipeStr], aliasMap);
|
|
3537
|
+
Object.assign(table.metadata, meta);
|
|
3538
|
+
}
|
|
3486
3539
|
currentTable = table;
|
|
3487
3540
|
continue;
|
|
3488
3541
|
}
|
|
@@ -3492,6 +3545,27 @@ function parseERDiagram(content, palette) {
|
|
|
3492
3545
|
result.diagnostics.push(diag);
|
|
3493
3546
|
result.error = formatDgmoError(diag);
|
|
3494
3547
|
}
|
|
3548
|
+
if (result.tagGroups.length > 0) {
|
|
3549
|
+
const tagEntities = result.tables.map((t) => ({
|
|
3550
|
+
metadata: t.metadata,
|
|
3551
|
+
lineNumber: t.lineNumber
|
|
3552
|
+
}));
|
|
3553
|
+
validateTagValues(
|
|
3554
|
+
tagEntities,
|
|
3555
|
+
result.tagGroups,
|
|
3556
|
+
(line10, msg) => result.diagnostics.push(makeDgmoError(line10, msg, "warning")),
|
|
3557
|
+
suggest
|
|
3558
|
+
);
|
|
3559
|
+
for (const group of result.tagGroups) {
|
|
3560
|
+
if (!group.defaultValue) continue;
|
|
3561
|
+
const key = group.name.toLowerCase();
|
|
3562
|
+
for (const table of result.tables) {
|
|
3563
|
+
if (!table.metadata[key]) {
|
|
3564
|
+
table.metadata[key] = group.defaultValue;
|
|
3565
|
+
}
|
|
3566
|
+
}
|
|
3567
|
+
}
|
|
3568
|
+
}
|
|
3495
3569
|
if (result.tables.length >= 2 && result.relationships.length >= 1 && !result.error) {
|
|
3496
3570
|
const connectedIds = /* @__PURE__ */ new Set();
|
|
3497
3571
|
for (const rel of result.relationships) {
|
|
@@ -3543,7 +3617,8 @@ var init_parser3 = __esm({
|
|
|
3543
3617
|
init_colors();
|
|
3544
3618
|
init_diagnostics();
|
|
3545
3619
|
init_parsing();
|
|
3546
|
-
|
|
3620
|
+
init_tag_groups();
|
|
3621
|
+
TABLE_DECL_RE = /^([a-zA-Z_]\w*)(?:\s*\(([^)]+)\))?(?:\s*\|(.+))?$/;
|
|
3547
3622
|
COLUMN_RE = /^(\w+)(?:\s*:\s*(\w[\w()]*(?:\s*\[\])?))?(?:\s+\[([^\]]+)\])?\s*$/;
|
|
3548
3623
|
INDENT_REL_RE = /^([1*?])-(?:(.+)-)?([1*?])\s+([a-zA-Z_]\w*)\s*$/;
|
|
3549
3624
|
CONSTRAINT_MAP = {
|
|
@@ -3581,7 +3656,10 @@ function parseChart(content, palette) {
|
|
|
3581
3656
|
const trimmed = lines[i].trim();
|
|
3582
3657
|
const lineNumber = i + 1;
|
|
3583
3658
|
if (!trimmed) continue;
|
|
3584
|
-
if (/^#{2,}\s+/.test(trimmed))
|
|
3659
|
+
if (/^#{2,}\s+/.test(trimmed)) {
|
|
3660
|
+
result.diagnostics.push(makeDgmoError(lineNumber, `'${trimmed}' \u2014 ## syntax is no longer supported. Use [Group] containers instead`));
|
|
3661
|
+
continue;
|
|
3662
|
+
}
|
|
3585
3663
|
if (trimmed.startsWith("//")) continue;
|
|
3586
3664
|
const colonIndex = trimmed.indexOf(":");
|
|
3587
3665
|
if (colonIndex === -1) continue;
|
|
@@ -3729,9 +3807,16 @@ function parseEChart(content, palette) {
|
|
|
3729
3807
|
const trimmed = lines[i].trim();
|
|
3730
3808
|
const lineNumber = i + 1;
|
|
3731
3809
|
if (!trimmed) continue;
|
|
3732
|
-
|
|
3733
|
-
|
|
3734
|
-
|
|
3810
|
+
if (/^#{2,}\s+/.test(trimmed)) {
|
|
3811
|
+
const name = trimmed.replace(/^#{2,}\s+/, "").replace(/\s*\([^)]*\)\s*$/, "").trim();
|
|
3812
|
+
result.diagnostics.push(makeDgmoError(lineNumber, `'## ${name}' is no longer supported. Use '[${name}]' instead`));
|
|
3813
|
+
continue;
|
|
3814
|
+
}
|
|
3815
|
+
if (trimmed.startsWith("//")) continue;
|
|
3816
|
+
const categoryMatch = trimmed.match(/^\[(.+?)\](?:\s*\(([^)]+)\))?\s*$/);
|
|
3817
|
+
if (categoryMatch) {
|
|
3818
|
+
const catName = categoryMatch[1].trim();
|
|
3819
|
+
const catColor = categoryMatch[2] ? resolveColor(categoryMatch[2].trim(), palette) : null;
|
|
3735
3820
|
if (catColor) {
|
|
3736
3821
|
if (!result.categoryColors) result.categoryColors = {};
|
|
3737
3822
|
result.categoryColors[catName] = catColor;
|
|
@@ -3739,12 +3824,6 @@ function parseEChart(content, palette) {
|
|
|
3739
3824
|
currentCategory = catName;
|
|
3740
3825
|
continue;
|
|
3741
3826
|
}
|
|
3742
|
-
if (trimmed.startsWith("//")) continue;
|
|
3743
|
-
const categoryMatch = trimmed.match(/^\[(.+)\]$/);
|
|
3744
|
-
if (categoryMatch) {
|
|
3745
|
-
currentCategory = categoryMatch[1].trim();
|
|
3746
|
-
continue;
|
|
3747
|
-
}
|
|
3748
3827
|
const colonIndex = trimmed.indexOf(":");
|
|
3749
3828
|
if (result.type === "sankey" && colonIndex === -1) {
|
|
3750
3829
|
const indent = measureIndent(lines[i]);
|
|
@@ -5375,6 +5454,7 @@ function parseKanban(content, palette) {
|
|
|
5375
5454
|
let currentTagGroup = null;
|
|
5376
5455
|
let currentColumn = null;
|
|
5377
5456
|
let currentCard = null;
|
|
5457
|
+
let cardBaseIndent = 0;
|
|
5378
5458
|
let columnCounter = 0;
|
|
5379
5459
|
let cardCounter = 0;
|
|
5380
5460
|
const aliasMap = /* @__PURE__ */ new Map();
|
|
@@ -5474,7 +5554,14 @@ function parseKanban(content, palette) {
|
|
|
5474
5554
|
}
|
|
5475
5555
|
currentTagGroup = null;
|
|
5476
5556
|
}
|
|
5477
|
-
const
|
|
5557
|
+
const indent = measureIndent(line10);
|
|
5558
|
+
if (LEGACY_COLUMN_RE.test(trimmed)) {
|
|
5559
|
+
const legacyMatch = trimmed.match(LEGACY_COLUMN_RE);
|
|
5560
|
+
const name = legacyMatch[1].replace(/\s*\(.*\)\s*$/, "").trim();
|
|
5561
|
+
warn(lineNumber, `'== ${name} ==' is no longer supported. Use '[${name}]' instead`);
|
|
5562
|
+
continue;
|
|
5563
|
+
}
|
|
5564
|
+
const columnMatch = indent === 0 ? trimmed.match(COLUMN_RE2) : null;
|
|
5478
5565
|
if (columnMatch) {
|
|
5479
5566
|
contentStarted = true;
|
|
5480
5567
|
currentTagGroup = null;
|
|
@@ -5486,16 +5573,20 @@ function parseKanban(content, palette) {
|
|
|
5486
5573
|
}
|
|
5487
5574
|
currentCard = null;
|
|
5488
5575
|
columnCounter++;
|
|
5489
|
-
const
|
|
5490
|
-
const
|
|
5491
|
-
|
|
5492
|
-
|
|
5493
|
-
|
|
5494
|
-
|
|
5576
|
+
const colName = columnMatch[1].trim();
|
|
5577
|
+
const colColor = columnMatch[2] ? resolveColor(columnMatch[2].trim(), palette) : void 0;
|
|
5578
|
+
let wipLimit;
|
|
5579
|
+
const pipeStr = columnMatch[3];
|
|
5580
|
+
if (pipeStr) {
|
|
5581
|
+
const wipMatch = pipeStr.match(/\bwip\s*:\s*(\d+)\b/i);
|
|
5582
|
+
if (wipMatch) {
|
|
5583
|
+
wipLimit = parseInt(wipMatch[1], 10);
|
|
5584
|
+
}
|
|
5585
|
+
}
|
|
5495
5586
|
currentColumn = {
|
|
5496
5587
|
id: `col-${columnCounter}`,
|
|
5497
5588
|
name: colName,
|
|
5498
|
-
wipLimit
|
|
5589
|
+
wipLimit,
|
|
5499
5590
|
color: colColor,
|
|
5500
5591
|
cards: [],
|
|
5501
5592
|
lineNumber
|
|
@@ -5510,24 +5601,25 @@ function parseKanban(content, palette) {
|
|
|
5510
5601
|
warn(lineNumber, "Card line found before any column");
|
|
5511
5602
|
continue;
|
|
5512
5603
|
}
|
|
5513
|
-
|
|
5514
|
-
if (indent > 0 && currentCard) {
|
|
5604
|
+
if (currentCard && indent > cardBaseIndent) {
|
|
5515
5605
|
currentCard.details.push(trimmed);
|
|
5516
5606
|
currentCard.endLineNumber = lineNumber;
|
|
5517
5607
|
continue;
|
|
5518
5608
|
}
|
|
5519
|
-
if (
|
|
5609
|
+
if (indent > 0) {
|
|
5610
|
+
cardCounter++;
|
|
5611
|
+
const card = parseCardLine(
|
|
5612
|
+
trimmed,
|
|
5613
|
+
lineNumber,
|
|
5614
|
+
cardCounter,
|
|
5615
|
+
aliasMap,
|
|
5616
|
+
palette
|
|
5617
|
+
);
|
|
5618
|
+
cardBaseIndent = indent;
|
|
5619
|
+
currentCard = card;
|
|
5620
|
+
currentColumn.cards.push(card);
|
|
5621
|
+
continue;
|
|
5520
5622
|
}
|
|
5521
|
-
cardCounter++;
|
|
5522
|
-
const card = parseCardLine(
|
|
5523
|
-
trimmed,
|
|
5524
|
-
lineNumber,
|
|
5525
|
-
cardCounter,
|
|
5526
|
-
aliasMap,
|
|
5527
|
-
palette
|
|
5528
|
-
);
|
|
5529
|
-
currentCard = card;
|
|
5530
|
-
currentColumn.cards.push(card);
|
|
5531
5623
|
}
|
|
5532
5624
|
if (currentCard) {
|
|
5533
5625
|
}
|
|
@@ -5561,7 +5653,7 @@ function parseKanban(content, palette) {
|
|
|
5561
5653
|
}
|
|
5562
5654
|
}
|
|
5563
5655
|
if (result.columns.length === 0 && !result.error) {
|
|
5564
|
-
return fail(1, "No columns found. Use
|
|
5656
|
+
return fail(1, "No columns found. Use [Column Name] to define columns");
|
|
5565
5657
|
}
|
|
5566
5658
|
return result;
|
|
5567
5659
|
}
|
|
@@ -5598,14 +5690,16 @@ function parseCardLine(trimmed, lineNumber, counter, aliasMap, palette) {
|
|
|
5598
5690
|
color
|
|
5599
5691
|
};
|
|
5600
5692
|
}
|
|
5601
|
-
var COLUMN_RE2;
|
|
5693
|
+
var COLUMN_RE2, LEGACY_COLUMN_RE;
|
|
5602
5694
|
var init_parser5 = __esm({
|
|
5603
5695
|
"src/kanban/parser.ts"() {
|
|
5604
5696
|
"use strict";
|
|
5605
5697
|
init_diagnostics();
|
|
5698
|
+
init_colors();
|
|
5606
5699
|
init_tag_groups();
|
|
5607
5700
|
init_parsing();
|
|
5608
|
-
COLUMN_RE2 =
|
|
5701
|
+
COLUMN_RE2 = /^\[(.+?)\](?:\s*\(([^)]+)\))?\s*(?:\|\s*(.+))?$/;
|
|
5702
|
+
LEGACY_COLUMN_RE = /^==\s+(.+?)\s*(?:\[wip:\s*(\d+)\])?\s*==$/;
|
|
5609
5703
|
}
|
|
5610
5704
|
});
|
|
5611
5705
|
|
|
@@ -9556,7 +9650,7 @@ function computeCardArchive(content, parsed, cardId) {
|
|
|
9556
9650
|
const trimmedEnd = withoutCard.length > 0 && withoutCard[withoutCard.length - 1].trim() === "" ? withoutCard : [...withoutCard, ""];
|
|
9557
9651
|
return [
|
|
9558
9652
|
...trimmedEnd,
|
|
9559
|
-
"
|
|
9653
|
+
"[Archive]",
|
|
9560
9654
|
...cardLines
|
|
9561
9655
|
].join("\n");
|
|
9562
9656
|
}
|
|
@@ -10437,7 +10531,7 @@ function drawCardinality(g, point, prevPoint, cardinality, color, useLabels) {
|
|
|
10437
10531
|
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);
|
|
10438
10532
|
}
|
|
10439
10533
|
}
|
|
10440
|
-
function renderERDiagram(container, parsed, layout, palette, isDark, onClickItem, exportDims) {
|
|
10534
|
+
function renderERDiagram(container, parsed, layout, palette, isDark, onClickItem, exportDims, activeTagGroup) {
|
|
10441
10535
|
d3Selection5.select(container).selectAll(":not([data-d3-tooltip])").remove();
|
|
10442
10536
|
const width = exportDims?.width ?? container.clientWidth;
|
|
10443
10537
|
const height = exportDims?.height ?? container.clientHeight;
|
|
@@ -10507,8 +10601,16 @@ function renderERDiagram(container, parsed, layout, palette, isDark, onClickItem
|
|
|
10507
10601
|
}
|
|
10508
10602
|
for (let ni = 0; ni < layout.nodes.length; ni++) {
|
|
10509
10603
|
const node = layout.nodes[ni];
|
|
10510
|
-
const
|
|
10604
|
+
const tagColor = resolveTagColor(node.metadata, parsed.tagGroups, activeTagGroup ?? null);
|
|
10605
|
+
const nodeColor2 = node.color ?? tagColor ?? seriesColors2[ni % seriesColors2.length];
|
|
10511
10606
|
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);
|
|
10607
|
+
if (activeTagGroup) {
|
|
10608
|
+
const tagKey = activeTagGroup.toLowerCase();
|
|
10609
|
+
const tagValue = node.metadata[tagKey];
|
|
10610
|
+
if (tagValue) {
|
|
10611
|
+
nodeG.attr(`data-tag-${tagKey}`, tagValue.toLowerCase());
|
|
10612
|
+
}
|
|
10613
|
+
}
|
|
10512
10614
|
if (onClickItem) {
|
|
10513
10615
|
nodeG.style("cursor", "pointer").on("click", () => {
|
|
10514
10616
|
onClickItem(node.lineNumber);
|
|
@@ -10540,6 +10642,35 @@ function renderERDiagram(container, parsed, layout, palette, isDark, onClickItem
|
|
|
10540
10642
|
}
|
|
10541
10643
|
}
|
|
10542
10644
|
}
|
|
10645
|
+
if (parsed.tagGroups.length > 0) {
|
|
10646
|
+
const LEGEND_Y_PAD = 16;
|
|
10647
|
+
const LEGEND_PILL_H = 22;
|
|
10648
|
+
const LEGEND_PILL_RX = 11;
|
|
10649
|
+
const LEGEND_PILL_PAD9 = 10;
|
|
10650
|
+
const LEGEND_GAP2 = 8;
|
|
10651
|
+
const LEGEND_FONT_SIZE2 = 11;
|
|
10652
|
+
const LEGEND_GROUP_GAP7 = 16;
|
|
10653
|
+
const legendG = svg.append("g").attr("class", "er-tag-legend");
|
|
10654
|
+
let legendX = DIAGRAM_PADDING5;
|
|
10655
|
+
let legendY = height - DIAGRAM_PADDING5;
|
|
10656
|
+
for (const group of parsed.tagGroups) {
|
|
10657
|
+
const groupG = legendG.append("g").attr("data-legend-group", group.name.toLowerCase());
|
|
10658
|
+
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}:`);
|
|
10659
|
+
const labelWidth = (labelText.node()?.getComputedTextLength?.() ?? group.name.length * 7) + 6;
|
|
10660
|
+
legendX += labelWidth;
|
|
10661
|
+
for (const entry of group.entries) {
|
|
10662
|
+
const pillG = groupG.append("g").attr("data-legend-entry", entry.value.toLowerCase()).style("cursor", "pointer");
|
|
10663
|
+
const tmpText = legendG.append("text").attr("font-size", LEGEND_FONT_SIZE2).attr("font-family", FONT_FAMILY).text(entry.value);
|
|
10664
|
+
const textW = tmpText.node()?.getComputedTextLength?.() ?? entry.value.length * 7;
|
|
10665
|
+
tmpText.remove();
|
|
10666
|
+
const pillW = textW + LEGEND_PILL_PAD9 * 2;
|
|
10667
|
+
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);
|
|
10668
|
+
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);
|
|
10669
|
+
legendX += pillW + LEGEND_GAP2;
|
|
10670
|
+
}
|
|
10671
|
+
legendX += LEGEND_GROUP_GAP7;
|
|
10672
|
+
}
|
|
10673
|
+
}
|
|
10543
10674
|
}
|
|
10544
10675
|
function renderERDiagramForExport(content, theme, palette) {
|
|
10545
10676
|
const parsed = parseERDiagram(content, palette);
|
|
@@ -10583,6 +10714,7 @@ var init_renderer5 = __esm({
|
|
|
10583
10714
|
init_fonts();
|
|
10584
10715
|
init_color_utils();
|
|
10585
10716
|
init_palettes();
|
|
10717
|
+
init_tag_groups();
|
|
10586
10718
|
init_parser3();
|
|
10587
10719
|
init_layout4();
|
|
10588
10720
|
DIAGRAM_PADDING5 = 20;
|
|
@@ -17989,6 +18121,7 @@ function parseD3(content, palette) {
|
|
|
17989
18121
|
timelineGroups: [],
|
|
17990
18122
|
timelineEras: [],
|
|
17991
18123
|
timelineMarkers: [],
|
|
18124
|
+
timelineTagGroups: [],
|
|
17992
18125
|
timelineSort: "time",
|
|
17993
18126
|
timelineScale: true,
|
|
17994
18127
|
timelineSwimlanes: false,
|
|
@@ -18023,25 +18156,75 @@ function parseD3(content, palette) {
|
|
|
18023
18156
|
const freeformLines = [];
|
|
18024
18157
|
let currentArcGroup = null;
|
|
18025
18158
|
let currentTimelineGroup = null;
|
|
18159
|
+
let currentTimelineTagGroup = null;
|
|
18160
|
+
const timelineAliasMap = /* @__PURE__ */ new Map();
|
|
18026
18161
|
for (let i = 0; i < lines.length; i++) {
|
|
18027
|
-
const
|
|
18162
|
+
const rawLine = lines[i];
|
|
18163
|
+
const line10 = rawLine.trim();
|
|
18164
|
+
const indent = rawLine.length - rawLine.trimStart().length;
|
|
18028
18165
|
const lineNumber = i + 1;
|
|
18029
18166
|
if (!line10) continue;
|
|
18030
|
-
|
|
18031
|
-
|
|
18167
|
+
if (result.type === "timeline" && indent === 0) {
|
|
18168
|
+
const tagBlockMatch = matchTagBlockHeading(line10);
|
|
18169
|
+
if (tagBlockMatch) {
|
|
18170
|
+
if (tagBlockMatch.deprecated) {
|
|
18171
|
+
result.diagnostics.push(makeDgmoError(
|
|
18172
|
+
lineNumber,
|
|
18173
|
+
`'## ${tagBlockMatch.name}' is deprecated for tag groups \u2014 use 'tag: ${tagBlockMatch.name}' instead`,
|
|
18174
|
+
"warning"
|
|
18175
|
+
));
|
|
18176
|
+
}
|
|
18177
|
+
currentTimelineTagGroup = {
|
|
18178
|
+
name: tagBlockMatch.name,
|
|
18179
|
+
alias: tagBlockMatch.alias,
|
|
18180
|
+
entries: [],
|
|
18181
|
+
lineNumber
|
|
18182
|
+
};
|
|
18183
|
+
if (tagBlockMatch.alias) {
|
|
18184
|
+
timelineAliasMap.set(tagBlockMatch.alias.toLowerCase(), tagBlockMatch.name.toLowerCase());
|
|
18185
|
+
}
|
|
18186
|
+
result.timelineTagGroups.push(currentTimelineTagGroup);
|
|
18187
|
+
continue;
|
|
18188
|
+
}
|
|
18189
|
+
}
|
|
18190
|
+
if (currentTimelineTagGroup && indent > 0) {
|
|
18191
|
+
const trimmedEntry = line10;
|
|
18192
|
+
const isDefault = /\bdefault\s*$/.test(trimmedEntry);
|
|
18193
|
+
const entryText = isDefault ? trimmedEntry.replace(/\s+default\s*$/, "").trim() : trimmedEntry;
|
|
18194
|
+
const { label, color } = extractColor(entryText, palette);
|
|
18195
|
+
if (color) {
|
|
18196
|
+
if (isDefault) currentTimelineTagGroup.defaultValue = label;
|
|
18197
|
+
currentTimelineTagGroup.entries.push({ value: label, color, lineNumber });
|
|
18198
|
+
continue;
|
|
18199
|
+
}
|
|
18200
|
+
}
|
|
18201
|
+
if (currentTimelineTagGroup && indent === 0) {
|
|
18202
|
+
currentTimelineTagGroup = null;
|
|
18203
|
+
}
|
|
18204
|
+
const groupMatch = line10.match(/^\[(.+?)\](?:\s*\(([^)]+)\))?\s*$/);
|
|
18205
|
+
if (groupMatch) {
|
|
18032
18206
|
if (result.type === "arc") {
|
|
18033
|
-
const name =
|
|
18034
|
-
const color =
|
|
18207
|
+
const name = groupMatch[1].trim();
|
|
18208
|
+
const color = groupMatch[2] ? resolveColor(groupMatch[2].trim(), palette) : null;
|
|
18035
18209
|
result.arcNodeGroups.push({ name, nodes: [], color, lineNumber });
|
|
18036
18210
|
currentArcGroup = name;
|
|
18037
18211
|
} else if (result.type === "timeline") {
|
|
18038
|
-
const name =
|
|
18039
|
-
const color =
|
|
18212
|
+
const name = groupMatch[1].trim();
|
|
18213
|
+
const color = groupMatch[2] ? resolveColor(groupMatch[2].trim(), palette) : null;
|
|
18040
18214
|
result.timelineGroups.push({ name, color, lineNumber });
|
|
18041
18215
|
currentTimelineGroup = name;
|
|
18042
18216
|
}
|
|
18043
18217
|
continue;
|
|
18044
18218
|
}
|
|
18219
|
+
if (/^#{2,}\s+/.test(line10) && (result.type === "arc" || result.type === "timeline")) {
|
|
18220
|
+
const name = line10.replace(/^#{2,}\s+/, "").replace(/\s*\([^)]*\)\s*$/, "").trim();
|
|
18221
|
+
result.diagnostics.push(makeDgmoError(lineNumber, `'## ${name}' is no longer supported. Use '[${name}]' instead`, "warning"));
|
|
18222
|
+
continue;
|
|
18223
|
+
}
|
|
18224
|
+
if (indent === 0) {
|
|
18225
|
+
currentArcGroup = null;
|
|
18226
|
+
currentTimelineGroup = null;
|
|
18227
|
+
}
|
|
18045
18228
|
if (line10.startsWith("//")) {
|
|
18046
18229
|
continue;
|
|
18047
18230
|
}
|
|
@@ -18113,11 +18296,14 @@ function parseD3(content, palette) {
|
|
|
18113
18296
|
const amount = parseFloat(durationMatch[2]);
|
|
18114
18297
|
const unit = durationMatch[3];
|
|
18115
18298
|
const endDate = addDurationToDate(startDate, amount, unit);
|
|
18299
|
+
const segments = durationMatch[5].split("|");
|
|
18300
|
+
const metadata = segments.length > 1 ? parsePipeMetadata(["", ...segments.slice(1)], timelineAliasMap) : {};
|
|
18116
18301
|
result.timelineEvents.push({
|
|
18117
18302
|
date: startDate,
|
|
18118
18303
|
endDate,
|
|
18119
|
-
label:
|
|
18304
|
+
label: segments[0].trim(),
|
|
18120
18305
|
group: currentTimelineGroup,
|
|
18306
|
+
metadata,
|
|
18121
18307
|
lineNumber,
|
|
18122
18308
|
uncertain
|
|
18123
18309
|
});
|
|
@@ -18127,11 +18313,14 @@ function parseD3(content, palette) {
|
|
|
18127
18313
|
/^(\d{4}(?:-\d{2})?(?:-\d{2})?)\s*->\s*(\d{4}(?:-\d{2})?(?:-\d{2})?)(\?)?\s*:\s*(.+)$/
|
|
18128
18314
|
);
|
|
18129
18315
|
if (rangeMatch) {
|
|
18316
|
+
const segments = rangeMatch[4].split("|");
|
|
18317
|
+
const metadata = segments.length > 1 ? parsePipeMetadata(["", ...segments.slice(1)], timelineAliasMap) : {};
|
|
18130
18318
|
result.timelineEvents.push({
|
|
18131
18319
|
date: rangeMatch[1],
|
|
18132
18320
|
endDate: rangeMatch[2],
|
|
18133
|
-
label:
|
|
18321
|
+
label: segments[0].trim(),
|
|
18134
18322
|
group: currentTimelineGroup,
|
|
18323
|
+
metadata,
|
|
18135
18324
|
lineNumber,
|
|
18136
18325
|
uncertain: rangeMatch[3] === "?"
|
|
18137
18326
|
});
|
|
@@ -18141,11 +18330,14 @@ function parseD3(content, palette) {
|
|
|
18141
18330
|
/^(\d{4}(?:-\d{2})?(?:-\d{2})?)\s*:\s*(.+)$/
|
|
18142
18331
|
);
|
|
18143
18332
|
if (pointMatch) {
|
|
18333
|
+
const segments = pointMatch[2].split("|");
|
|
18334
|
+
const metadata = segments.length > 1 ? parsePipeMetadata(["", ...segments.slice(1)], timelineAliasMap) : {};
|
|
18144
18335
|
result.timelineEvents.push({
|
|
18145
18336
|
date: pointMatch[1],
|
|
18146
18337
|
endDate: null,
|
|
18147
|
-
label:
|
|
18338
|
+
label: segments[0].trim(),
|
|
18148
18339
|
group: currentTimelineGroup,
|
|
18340
|
+
metadata,
|
|
18149
18341
|
lineNumber
|
|
18150
18342
|
});
|
|
18151
18343
|
continue;
|
|
@@ -18286,9 +18478,18 @@ function parseD3(content, palette) {
|
|
|
18286
18478
|
continue;
|
|
18287
18479
|
}
|
|
18288
18480
|
if (key === "sort") {
|
|
18289
|
-
const v = line10.substring(colonIndex + 1).trim()
|
|
18290
|
-
|
|
18291
|
-
|
|
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
|
+
}
|
|
18292
18493
|
}
|
|
18293
18494
|
continue;
|
|
18294
18495
|
}
|
|
@@ -18408,7 +18609,7 @@ function parseD3(content, palette) {
|
|
|
18408
18609
|
}
|
|
18409
18610
|
if (result.arcNodeGroups.length > 0) {
|
|
18410
18611
|
if (result.arcOrder === "name" || result.arcOrder === "degree") {
|
|
18411
|
-
warn(1, `Cannot use "order: ${result.arcOrder}" with
|
|
18612
|
+
warn(1, `Cannot use "order: ${result.arcOrder}" with [Group] headers. Use "order: group" or remove group headers.`);
|
|
18412
18613
|
result.arcOrder = "group";
|
|
18413
18614
|
}
|
|
18414
18615
|
if (result.arcOrder === "appearance") {
|
|
@@ -18421,6 +18622,42 @@ function parseD3(content, palette) {
|
|
|
18421
18622
|
if (result.timelineEvents.length === 0) {
|
|
18422
18623
|
warn(1, 'No events found. Add events as "YYYY: description" or "YYYY->YYYY: description"');
|
|
18423
18624
|
}
|
|
18625
|
+
if (result.timelineTagGroups.length > 0) {
|
|
18626
|
+
validateTagValues(
|
|
18627
|
+
result.timelineEvents,
|
|
18628
|
+
result.timelineTagGroups,
|
|
18629
|
+
(line10, msg) => result.diagnostics.push(makeDgmoError(line10, msg, "warning")),
|
|
18630
|
+
suggest
|
|
18631
|
+
);
|
|
18632
|
+
for (const group of result.timelineTagGroups) {
|
|
18633
|
+
if (!group.defaultValue) continue;
|
|
18634
|
+
const key = group.name.toLowerCase();
|
|
18635
|
+
for (const event of result.timelineEvents) {
|
|
18636
|
+
if (!event.metadata[key]) {
|
|
18637
|
+
event.metadata[key] = group.defaultValue;
|
|
18638
|
+
}
|
|
18639
|
+
}
|
|
18640
|
+
}
|
|
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
|
+
}
|
|
18424
18661
|
return result;
|
|
18425
18662
|
}
|
|
18426
18663
|
if (result.type === "venn") {
|
|
@@ -19179,7 +19416,7 @@ function buildEventTooltipHtml(ev) {
|
|
|
19179
19416
|
function buildEraTooltipHtml(era) {
|
|
19180
19417
|
return `<strong>${era.label}</strong><br>${formatDateLabel(era.startDate)} \u2192 ${formatDateLabel(era.endDate)}`;
|
|
19181
19418
|
}
|
|
19182
|
-
function renderTimeline(container, parsed, palette, isDark, onClickItem, exportDims) {
|
|
19419
|
+
function renderTimeline(container, parsed, palette, isDark, onClickItem, exportDims, activeTagGroup, swimlaneTagGroup, onTagStateChange) {
|
|
19183
19420
|
d3Selection12.select(container).selectAll(":not([data-d3-tooltip])").remove();
|
|
19184
19421
|
const {
|
|
19185
19422
|
timelineEvents,
|
|
@@ -19193,6 +19430,9 @@ function renderTimeline(container, parsed, palette, isDark, onClickItem, exportD
|
|
|
19193
19430
|
orientation
|
|
19194
19431
|
} = parsed;
|
|
19195
19432
|
if (timelineEvents.length === 0) return;
|
|
19433
|
+
if (swimlaneTagGroup == null && timelineSort === "tag" && parsed.timelineDefaultSwimlaneTG) {
|
|
19434
|
+
swimlaneTagGroup = parsed.timelineDefaultSwimlaneTG;
|
|
19435
|
+
}
|
|
19196
19436
|
const tooltip = createTooltip(container, palette, isDark);
|
|
19197
19437
|
const width = exportDims?.width ?? container.clientWidth;
|
|
19198
19438
|
const height = exportDims?.height ?? container.clientHeight;
|
|
@@ -19206,7 +19446,49 @@ function renderTimeline(container, parsed, palette, isDark, onClickItem, exportD
|
|
|
19206
19446
|
timelineGroups.forEach((grp, i) => {
|
|
19207
19447
|
groupColorMap.set(grp.name, grp.color ?? colors[i % colors.length]);
|
|
19208
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;
|
|
19209
19487
|
function eventColor(ev) {
|
|
19488
|
+
if (effectiveColorTG) {
|
|
19489
|
+
const tagColor = resolveTagColor(ev.metadata, parsed.timelineTagGroups, effectiveColorTG);
|
|
19490
|
+
if (tagColor) return tagColor;
|
|
19491
|
+
}
|
|
19210
19492
|
if (ev.group && groupColorMap.has(ev.group)) {
|
|
19211
19493
|
return groupColorMap.get(ev.group);
|
|
19212
19494
|
}
|
|
@@ -19277,22 +19559,64 @@ function renderTimeline(container, parsed, palette, isDark, onClickItem, exportD
|
|
|
19277
19559
|
}
|
|
19278
19560
|
function fadeReset(g) {
|
|
19279
19561
|
g.selectAll(
|
|
19280
|
-
".tl-event, .tl-legend-item, .tl-lane-header, .tl-marker"
|
|
19562
|
+
".tl-event, .tl-legend-item, .tl-lane-header, .tl-marker, .tl-tag-legend-entry"
|
|
19281
19563
|
).attr("opacity", 1);
|
|
19282
19564
|
g.selectAll(".tl-era").attr("opacity", 1);
|
|
19283
19565
|
}
|
|
19566
|
+
function fadeToTagValue(g, tagKey, tagValue) {
|
|
19567
|
+
const attrName = `data-tag-${tagKey}`;
|
|
19568
|
+
g.selectAll(".tl-event").each(function() {
|
|
19569
|
+
const el = d3Selection12.select(this);
|
|
19570
|
+
const val = el.attr(attrName);
|
|
19571
|
+
el.attr("opacity", val === tagValue ? 1 : FADE_OPACITY);
|
|
19572
|
+
});
|
|
19573
|
+
g.selectAll(".tl-legend-item, .tl-lane-header").attr(
|
|
19574
|
+
"opacity",
|
|
19575
|
+
FADE_OPACITY
|
|
19576
|
+
);
|
|
19577
|
+
g.selectAll(".tl-marker").attr("opacity", FADE_OPACITY);
|
|
19578
|
+
g.selectAll(".tl-tag-legend-entry").each(function() {
|
|
19579
|
+
const el = d3Selection12.select(this);
|
|
19580
|
+
const entryValue = el.attr("data-legend-entry");
|
|
19581
|
+
if (entryValue === "__group__") return;
|
|
19582
|
+
const entryGroup = el.attr("data-tag-group");
|
|
19583
|
+
el.attr("opacity", entryGroup === tagKey && entryValue === tagValue ? 1 : FADE_OPACITY);
|
|
19584
|
+
});
|
|
19585
|
+
}
|
|
19586
|
+
function setTagAttrs(evG, ev) {
|
|
19587
|
+
for (const [key, value] of Object.entries(ev.metadata)) {
|
|
19588
|
+
evG.attr(`data-tag-${key}`, value.toLowerCase());
|
|
19589
|
+
}
|
|
19590
|
+
}
|
|
19591
|
+
const tagLegendReserve = parsed.timelineTagGroups.length > 0 ? 36 : 0;
|
|
19284
19592
|
if (isVertical) {
|
|
19285
|
-
|
|
19286
|
-
|
|
19287
|
-
|
|
19288
|
-
|
|
19289
|
-
)
|
|
19290
|
-
|
|
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
|
+
}
|
|
19291
19615
|
const laneCount = laneNames.length;
|
|
19292
19616
|
const scaleMargin = timelineScale ? 40 : 0;
|
|
19293
19617
|
const markerMargin = timelineMarkers.length > 0 ? 30 : 0;
|
|
19294
19618
|
const margin = {
|
|
19295
|
-
top: 104 + markerMargin,
|
|
19619
|
+
top: 104 + markerMargin + tagLegendReserve,
|
|
19296
19620
|
right: 40 + scaleMargin,
|
|
19297
19621
|
bottom: 40,
|
|
19298
19622
|
left: 60 + scaleMargin
|
|
@@ -19342,6 +19666,13 @@ function renderTimeline(container, parsed, palette, isDark, onClickItem, exportD
|
|
|
19342
19666
|
formatDateLabel(latestEndDateStr)
|
|
19343
19667
|
);
|
|
19344
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
|
+
}
|
|
19345
19676
|
laneNames.forEach((laneName, laneIdx) => {
|
|
19346
19677
|
const laneX = laneIdx * laneWidth;
|
|
19347
19678
|
const laneColor = groupColorMap.get(laneName) ?? textColor;
|
|
@@ -19349,9 +19680,7 @@ function renderTimeline(container, parsed, palette, isDark, onClickItem, exportD
|
|
|
19349
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));
|
|
19350
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);
|
|
19351
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");
|
|
19352
|
-
const laneEvents =
|
|
19353
|
-
(ev) => laneName === "(Other)" ? ev.group === null || !groupNames.includes(ev.group) : ev.group === laneName
|
|
19354
|
-
);
|
|
19683
|
+
const laneEvents = laneEventsByName.get(laneName) ?? [];
|
|
19355
19684
|
for (const ev of laneEvents) {
|
|
19356
19685
|
const y = yScale(parseTimelineDate(ev.date));
|
|
19357
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(
|
|
@@ -19368,10 +19697,12 @@ function renderTimeline(container, parsed, palette, isDark, onClickItem, exportD
|
|
|
19368
19697
|
}).on("click", () => {
|
|
19369
19698
|
if (onClickItem && ev.lineNumber) onClickItem(ev.lineNumber);
|
|
19370
19699
|
});
|
|
19700
|
+
setTagAttrs(evG, ev);
|
|
19701
|
+
const evColor = eventColor(ev);
|
|
19371
19702
|
if (ev.endDate) {
|
|
19372
19703
|
const y2 = yScale(parseTimelineDate(ev.endDate));
|
|
19373
19704
|
const rectH = Math.max(y2 - y, 4);
|
|
19374
|
-
let fill2 =
|
|
19705
|
+
let fill2 = evColor;
|
|
19375
19706
|
if (ev.uncertain) {
|
|
19376
19707
|
const gradientId = `uncertain-vg-${ev.lineNumber}`;
|
|
19377
19708
|
const defs = svg.select("defs").node() || svg.append("defs").node();
|
|
@@ -19385,7 +19716,7 @@ function renderTimeline(container, parsed, palette, isDark, onClickItem, exportD
|
|
|
19385
19716
|
evG.append("rect").attr("x", laneCenter - 6).attr("y", y).attr("width", 12).attr("height", rectH).attr("rx", 4).attr("fill", fill2);
|
|
19386
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);
|
|
19387
19718
|
} else {
|
|
19388
|
-
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);
|
|
19389
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);
|
|
19390
19721
|
}
|
|
19391
19722
|
}
|
|
@@ -19394,7 +19725,7 @@ function renderTimeline(container, parsed, palette, isDark, onClickItem, exportD
|
|
|
19394
19725
|
const scaleMargin = timelineScale ? 40 : 0;
|
|
19395
19726
|
const markerMargin = timelineMarkers.length > 0 ? 30 : 0;
|
|
19396
19727
|
const margin = {
|
|
19397
|
-
top: 104 + markerMargin,
|
|
19728
|
+
top: 104 + markerMargin + tagLegendReserve,
|
|
19398
19729
|
right: 200,
|
|
19399
19730
|
bottom: 40,
|
|
19400
19731
|
left: 60 + scaleMargin
|
|
@@ -19474,6 +19805,7 @@ function renderTimeline(container, parsed, palette, isDark, onClickItem, exportD
|
|
|
19474
19805
|
}).on("click", () => {
|
|
19475
19806
|
if (onClickItem && ev.lineNumber) onClickItem(ev.lineNumber);
|
|
19476
19807
|
});
|
|
19808
|
+
setTagAttrs(evG, ev);
|
|
19477
19809
|
if (ev.endDate) {
|
|
19478
19810
|
const y2 = yScale(parseTimelineDate(ev.endDate));
|
|
19479
19811
|
const rectH = Math.max(y2 - y, 4);
|
|
@@ -19507,18 +19839,24 @@ function renderTimeline(container, parsed, palette, isDark, onClickItem, exportD
|
|
|
19507
19839
|
}
|
|
19508
19840
|
const BAR_H = 22;
|
|
19509
19841
|
const GROUP_GAP = 12;
|
|
19510
|
-
|
|
19511
|
-
|
|
19512
|
-
|
|
19513
|
-
|
|
19514
|
-
|
|
19515
|
-
|
|
19516
|
-
|
|
19517
|
-
|
|
19518
|
-
|
|
19519
|
-
|
|
19520
|
-
)
|
|
19521
|
-
|
|
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
|
+
}
|
|
19522
19860
|
const totalEventRows = lanes.reduce((s, l) => s + l.events.length, 0);
|
|
19523
19861
|
const scaleMargin = timelineScale ? 24 : 0;
|
|
19524
19862
|
const markerMargin = timelineMarkers.length > 0 ? 30 : 0;
|
|
@@ -19526,7 +19864,7 @@ function renderTimeline(container, parsed, palette, isDark, onClickItem, exportD
|
|
|
19526
19864
|
const dynamicLeftMargin = Math.max(120, maxGroupNameLen * 7 + 30);
|
|
19527
19865
|
const baseTopMargin = title ? 50 : 20;
|
|
19528
19866
|
const margin = {
|
|
19529
|
-
top: baseTopMargin + (timelineScale ? 40 : 0) + markerMargin,
|
|
19867
|
+
top: baseTopMargin + (timelineScale ? 40 : 0) + markerMargin + tagLegendReserve,
|
|
19530
19868
|
right: 40,
|
|
19531
19869
|
bottom: 40 + scaleMargin,
|
|
19532
19870
|
left: dynamicLeftMargin
|
|
@@ -19578,7 +19916,7 @@ function renderTimeline(container, parsed, palette, isDark, onClickItem, exportD
|
|
|
19578
19916
|
);
|
|
19579
19917
|
}
|
|
19580
19918
|
let curY = markerMargin;
|
|
19581
|
-
if (timelineSwimlanes) {
|
|
19919
|
+
if (timelineSwimlanes || tagLanes) {
|
|
19582
19920
|
let swimY = markerMargin;
|
|
19583
19921
|
lanes.forEach((lane, idx) => {
|
|
19584
19922
|
const laneSpan = lane.events.length * rowH;
|
|
@@ -19629,12 +19967,14 @@ function renderTimeline(container, parsed, palette, isDark, onClickItem, exportD
|
|
|
19629
19967
|
}).on("click", () => {
|
|
19630
19968
|
if (onClickItem && ev.lineNumber) onClickItem(ev.lineNumber);
|
|
19631
19969
|
});
|
|
19970
|
+
setTagAttrs(evG, ev);
|
|
19971
|
+
const evColor = eventColor(ev);
|
|
19632
19972
|
if (ev.endDate) {
|
|
19633
19973
|
const x2 = xScale(parseTimelineDate(ev.endDate));
|
|
19634
19974
|
const rectW = Math.max(x2 - x, 4);
|
|
19635
19975
|
const estLabelWidth = ev.label.length * 7 + 16;
|
|
19636
19976
|
const labelFitsInside = rectW >= estLabelWidth;
|
|
19637
|
-
let fill2 =
|
|
19977
|
+
let fill2 = evColor;
|
|
19638
19978
|
if (ev.uncertain) {
|
|
19639
19979
|
const gradientId = `uncertain-${ev.lineNumber}`;
|
|
19640
19980
|
const defs = svg.select("defs").node() || svg.append("defs").node();
|
|
@@ -19642,7 +19982,7 @@ function renderTimeline(container, parsed, palette, isDark, onClickItem, exportD
|
|
|
19642
19982
|
{ offset: "0%", opacity: 1 },
|
|
19643
19983
|
{ offset: "80%", opacity: 1 },
|
|
19644
19984
|
{ offset: "100%", opacity: 0 }
|
|
19645
|
-
]).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);
|
|
19646
19986
|
fill2 = `url(#${gradientId})`;
|
|
19647
19987
|
}
|
|
19648
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);
|
|
@@ -19659,7 +19999,7 @@ function renderTimeline(container, parsed, palette, isDark, onClickItem, exportD
|
|
|
19659
19999
|
const wouldFlipLeft = x > innerWidth * 0.6;
|
|
19660
20000
|
const labelFitsLeft = x - 10 - estLabelWidth > 0;
|
|
19661
20001
|
const flipLeft = wouldFlipLeft && labelFitsLeft;
|
|
19662
|
-
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);
|
|
19663
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);
|
|
19664
20004
|
}
|
|
19665
20005
|
});
|
|
@@ -19670,7 +20010,7 @@ function renderTimeline(container, parsed, palette, isDark, onClickItem, exportD
|
|
|
19670
20010
|
const scaleMargin = timelineScale ? 24 : 0;
|
|
19671
20011
|
const markerMargin = timelineMarkers.length > 0 ? 30 : 0;
|
|
19672
20012
|
const margin = {
|
|
19673
|
-
top: 104 + (timelineScale ? 40 : 0) + markerMargin,
|
|
20013
|
+
top: 104 + (timelineScale ? 40 : 0) + markerMargin + tagLegendReserve,
|
|
19674
20014
|
right: 40,
|
|
19675
20015
|
bottom: 40 + scaleMargin,
|
|
19676
20016
|
left: 60
|
|
@@ -19766,6 +20106,7 @@ function renderTimeline(container, parsed, palette, isDark, onClickItem, exportD
|
|
|
19766
20106
|
}).on("click", () => {
|
|
19767
20107
|
if (onClickItem && ev.lineNumber) onClickItem(ev.lineNumber);
|
|
19768
20108
|
});
|
|
20109
|
+
setTagAttrs(evG, ev);
|
|
19769
20110
|
if (ev.endDate) {
|
|
19770
20111
|
const x2 = xScale(parseTimelineDate(ev.endDate));
|
|
19771
20112
|
const rectW = Math.max(x2 - x, 4);
|
|
@@ -19801,6 +20142,163 @@ function renderTimeline(container, parsed, palette, isDark, onClickItem, exportD
|
|
|
19801
20142
|
}
|
|
19802
20143
|
});
|
|
19803
20144
|
}
|
|
20145
|
+
if (parsed.timelineTagGroups.length > 0) {
|
|
20146
|
+
const LG_HEIGHT = 28;
|
|
20147
|
+
const LG_PILL_PAD = 16;
|
|
20148
|
+
const LG_PILL_FONT_SIZE = 11;
|
|
20149
|
+
const LG_PILL_FONT_W = LG_PILL_FONT_SIZE * 0.6;
|
|
20150
|
+
const LG_CAPSULE_PAD = 4;
|
|
20151
|
+
const LG_DOT_R = 4;
|
|
20152
|
+
const LG_ENTRY_FONT_SIZE = 10;
|
|
20153
|
+
const LG_ENTRY_FONT_W = LG_ENTRY_FONT_SIZE * 0.6;
|
|
20154
|
+
const LG_ENTRY_DOT_GAP = 4;
|
|
20155
|
+
const LG_ENTRY_TRAIL = 8;
|
|
20156
|
+
const LG_GROUP_GAP = 12;
|
|
20157
|
+
const LG_ICON_W = 20;
|
|
20158
|
+
const mainSvg = d3Selection12.select(container).select("svg");
|
|
20159
|
+
const mainG = mainSvg.select("g");
|
|
20160
|
+
if (!mainSvg.empty() && !mainG.empty()) {
|
|
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() {
|
|
20187
|
+
mainSvg.selectAll(".tl-tag-legend-group").remove();
|
|
20188
|
+
const totalW = legendGroups.reduce((s, lg) => {
|
|
20189
|
+
const isActive = currentActiveGroup != null && lg.group.name.toLowerCase() === currentActiveGroup.toLowerCase();
|
|
20190
|
+
return s + (isActive ? lg.expandedWidth : lg.minifiedWidth);
|
|
20191
|
+
}, 0) + (legendGroups.length - 1) * LG_GROUP_GAP;
|
|
20192
|
+
let cx = (width - totalW) / 2;
|
|
20193
|
+
for (const lg of legendGroups) {
|
|
20194
|
+
const groupKey = lg.group.name.toLowerCase();
|
|
20195
|
+
const isActive = currentActiveGroup != null && currentActiveGroup.toLowerCase() === groupKey;
|
|
20196
|
+
const isSwimActive = currentSwimlaneGroup != null && currentSwimlaneGroup.toLowerCase() === groupKey;
|
|
20197
|
+
const pillLabel = lg.group.name;
|
|
20198
|
+
const pillWidth = pillLabel.length * LG_PILL_FONT_W + LG_PILL_PAD;
|
|
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", () => {
|
|
20200
|
+
currentActiveGroup = currentActiveGroup === groupKey ? null : groupKey;
|
|
20201
|
+
drawLegend2();
|
|
20202
|
+
recolorEvents2();
|
|
20203
|
+
onTagStateChange?.(currentActiveGroup, currentSwimlaneGroup);
|
|
20204
|
+
});
|
|
20205
|
+
if (isActive) {
|
|
20206
|
+
gEl.append("rect").attr("width", lg.expandedWidth).attr("height", LG_HEIGHT).attr("rx", LG_HEIGHT / 2).attr("fill", groupBg);
|
|
20207
|
+
}
|
|
20208
|
+
const pillXOff = isActive ? LG_CAPSULE_PAD : 0;
|
|
20209
|
+
const pillYOff = isActive ? LG_CAPSULE_PAD : 0;
|
|
20210
|
+
const pillH = LG_HEIGHT - (isActive ? LG_CAPSULE_PAD * 2 : 0);
|
|
20211
|
+
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);
|
|
20212
|
+
if (isActive) {
|
|
20213
|
+
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);
|
|
20214
|
+
}
|
|
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);
|
|
20216
|
+
if (isActive) {
|
|
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;
|
|
20227
|
+
for (const entry of lg.group.entries) {
|
|
20228
|
+
const tagKey = lg.group.name.toLowerCase();
|
|
20229
|
+
const tagVal = entry.value.toLowerCase();
|
|
20230
|
+
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) => {
|
|
20231
|
+
event.stopPropagation();
|
|
20232
|
+
fadeToTagValue(mainG, tagKey, tagVal);
|
|
20233
|
+
mainSvg.selectAll(".tl-tag-legend-entry").each(function() {
|
|
20234
|
+
const el = d3Selection12.select(this);
|
|
20235
|
+
const ev = el.attr("data-legend-entry");
|
|
20236
|
+
if (ev === "__group__") return;
|
|
20237
|
+
const eg = el.attr("data-tag-group");
|
|
20238
|
+
el.attr("opacity", eg === tagKey && ev === tagVal ? 1 : FADE_OPACITY);
|
|
20239
|
+
});
|
|
20240
|
+
}).on("mouseleave", (event) => {
|
|
20241
|
+
event.stopPropagation();
|
|
20242
|
+
fadeReset(mainG);
|
|
20243
|
+
mainSvg.selectAll(".tl-tag-legend-entry").attr("opacity", 1);
|
|
20244
|
+
}).on("click", (event) => {
|
|
20245
|
+
event.stopPropagation();
|
|
20246
|
+
});
|
|
20247
|
+
entryG.append("circle").attr("cx", entryX + LG_DOT_R).attr("cy", LG_HEIGHT / 2).attr("r", LG_DOT_R).attr("fill", entry.color);
|
|
20248
|
+
const textX = entryX + LG_DOT_R * 2 + LG_ENTRY_DOT_GAP;
|
|
20249
|
+
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);
|
|
20250
|
+
entryX = textX + entry.value.length * LG_ENTRY_FONT_W + LG_ENTRY_TRAIL;
|
|
20251
|
+
}
|
|
20252
|
+
}
|
|
20253
|
+
cx += (isActive ? lg.expandedWidth : lg.minifiedWidth) + LG_GROUP_GAP;
|
|
20254
|
+
}
|
|
20255
|
+
}, recolorEvents2 = function() {
|
|
20256
|
+
const colorTG = currentActiveGroup ?? swimlaneTagGroup ?? null;
|
|
20257
|
+
mainG.selectAll(".tl-event").each(function() {
|
|
20258
|
+
const el = d3Selection12.select(this);
|
|
20259
|
+
const lineNum = el.attr("data-line-number");
|
|
20260
|
+
const ev = lineNum ? eventByLine.get(lineNum) : void 0;
|
|
20261
|
+
if (!ev) return;
|
|
20262
|
+
let color;
|
|
20263
|
+
if (colorTG) {
|
|
20264
|
+
const tagColor = resolveTagColor(
|
|
20265
|
+
ev.metadata,
|
|
20266
|
+
parsed.timelineTagGroups,
|
|
20267
|
+
colorTG
|
|
20268
|
+
);
|
|
20269
|
+
color = tagColor ?? (ev.group && groupColorMap.has(ev.group) ? groupColorMap.get(ev.group) : textColor);
|
|
20270
|
+
} else {
|
|
20271
|
+
color = ev.group && groupColorMap.has(ev.group) ? groupColorMap.get(ev.group) : textColor;
|
|
20272
|
+
}
|
|
20273
|
+
el.selectAll("rect").attr("fill", color);
|
|
20274
|
+
el.selectAll("circle:not(.tl-event-point-outline)").attr("fill", color);
|
|
20275
|
+
});
|
|
20276
|
+
};
|
|
20277
|
+
var drawSwimlaneIcon = drawSwimlaneIcon2, relayout = relayout2, drawLegend = drawLegend2, recolorEvents = recolorEvents2;
|
|
20278
|
+
const legendY = title ? 50 : 10;
|
|
20279
|
+
const groupBg = isDark ? mix(palette.surface, palette.bg, 50) : mix(palette.surface, palette.bg, 30);
|
|
20280
|
+
const legendGroups = parsed.timelineTagGroups.map((g) => {
|
|
20281
|
+
const pillW = g.name.length * LG_PILL_FONT_W + LG_PILL_PAD;
|
|
20282
|
+
let entryX = LG_CAPSULE_PAD + pillW + LG_ICON_W + 4;
|
|
20283
|
+
for (const entry of g.entries) {
|
|
20284
|
+
const textX = entryX + LG_DOT_R * 2 + LG_ENTRY_DOT_GAP;
|
|
20285
|
+
entryX = textX + entry.value.length * LG_ENTRY_FONT_W + LG_ENTRY_TRAIL;
|
|
20286
|
+
}
|
|
20287
|
+
return {
|
|
20288
|
+
group: g,
|
|
20289
|
+
minifiedWidth: pillW,
|
|
20290
|
+
expandedWidth: entryX + LG_CAPSULE_PAD
|
|
20291
|
+
};
|
|
20292
|
+
});
|
|
20293
|
+
let currentActiveGroup = activeTagGroup ?? null;
|
|
20294
|
+
let currentSwimlaneGroup = swimlaneTagGroup ?? null;
|
|
20295
|
+
const eventByLine = /* @__PURE__ */ new Map();
|
|
20296
|
+
for (const ev of timelineEvents) {
|
|
20297
|
+
eventByLine.set(String(ev.lineNumber), ev);
|
|
20298
|
+
}
|
|
20299
|
+
drawLegend2();
|
|
20300
|
+
}
|
|
20301
|
+
}
|
|
19804
20302
|
}
|
|
19805
20303
|
function getRotateFn(mode) {
|
|
19806
20304
|
if (mode === "mixed") return () => Math.random() > 0.5 ? 0 : 90;
|
|
@@ -20768,7 +21266,16 @@ async function renderD3ForExport(content, theme, palette, orgExportState, option
|
|
|
20768
21266
|
} else if (parsed.type === "arc") {
|
|
20769
21267
|
renderArcDiagram(container, parsed, effectivePalette, isDark, void 0, dims);
|
|
20770
21268
|
} else if (parsed.type === "timeline") {
|
|
20771
|
-
renderTimeline(
|
|
21269
|
+
renderTimeline(
|
|
21270
|
+
container,
|
|
21271
|
+
parsed,
|
|
21272
|
+
effectivePalette,
|
|
21273
|
+
isDark,
|
|
21274
|
+
void 0,
|
|
21275
|
+
dims,
|
|
21276
|
+
orgExportState?.activeTagGroup,
|
|
21277
|
+
orgExportState?.swimlaneTagGroup
|
|
21278
|
+
);
|
|
20772
21279
|
} else if (parsed.type === "venn") {
|
|
20773
21280
|
renderVenn(container, parsed, effectivePalette, isDark, void 0, dims);
|
|
20774
21281
|
} else if (parsed.type === "quadrant") {
|
|
@@ -20786,8 +21293,10 @@ var init_d3 = __esm({
|
|
|
20786
21293
|
init_branding();
|
|
20787
21294
|
init_colors();
|
|
20788
21295
|
init_palettes();
|
|
21296
|
+
init_color_utils();
|
|
20789
21297
|
init_diagnostics();
|
|
20790
21298
|
init_parsing();
|
|
21299
|
+
init_tag_groups();
|
|
20791
21300
|
DEFAULT_CLOUD_OPTIONS = {
|
|
20792
21301
|
rotate: "none",
|
|
20793
21302
|
max: 0,
|
|
@@ -21649,6 +22158,9 @@ function encodeDiagramUrl(dsl, options) {
|
|
|
21649
22158
|
if (options?.viewState?.collapsedGroups?.length) {
|
|
21650
22159
|
hash += `&cg=${encodeURIComponent(options.viewState.collapsedGroups.join(","))}`;
|
|
21651
22160
|
}
|
|
22161
|
+
if (options?.viewState?.swimlaneTagGroup) {
|
|
22162
|
+
hash += `&swim=${encodeURIComponent(options.viewState.swimlaneTagGroup)}`;
|
|
22163
|
+
}
|
|
21652
22164
|
return { url: `${baseUrl}?${hash}#${hash}` };
|
|
21653
22165
|
}
|
|
21654
22166
|
function decodeDiagramUrl(hash) {
|
|
@@ -21672,6 +22184,9 @@ function decodeDiagramUrl(hash) {
|
|
|
21672
22184
|
if (key === "cg" && val) {
|
|
21673
22185
|
viewState.collapsedGroups = val.split(",").filter(Boolean);
|
|
21674
22186
|
}
|
|
22187
|
+
if (key === "swim" && val) {
|
|
22188
|
+
viewState.swimlaneTagGroup = val;
|
|
22189
|
+
}
|
|
21675
22190
|
}
|
|
21676
22191
|
if (payload.startsWith("dgmo=")) {
|
|
21677
22192
|
payload = payload.slice(5);
|