mirror of
https://github.com/mermaid-js/mermaid.git
synced 2025-08-15 06:19:24 +02:00
reset branch and readded commits
This commit is contained in:
@@ -46,6 +46,10 @@
|
||||
|
||||
%%
|
||||
|
||||
"click" return 'CLICK';
|
||||
"href" return 'HREF';
|
||||
\"[^"]*\" return 'STRING';
|
||||
|
||||
"default" return 'DEFAULT';
|
||||
|
||||
.*direction\s+TB[^\n]* return 'direction_tb';
|
||||
@@ -246,8 +250,26 @@ statement
|
||||
| direction
|
||||
| acc_title acc_title_value { $$=$2.trim();yy.setAccTitle($$); }
|
||||
| acc_descr acc_descr_value { $$=$2.trim();yy.setAccDescription($$); }
|
||||
| acc_descr_multiline_value { $$=$1.trim();yy.setAccDescription($$); } ;
|
||||
|
||||
| acc_descr_multiline_value { $$=$1.trim();yy.setAccDescription($$); }
|
||||
| CLICK idStatement STRING STRING NL
|
||||
{
|
||||
$$ = {
|
||||
stmt: "click",
|
||||
id: $2,
|
||||
url: $3,
|
||||
tooltip: $4
|
||||
};
|
||||
}
|
||||
| CLICK idStatement HREF STRING NL
|
||||
{
|
||||
$$ = {
|
||||
stmt: "click",
|
||||
id: $2,
|
||||
url: $4,
|
||||
tooltip: ""
|
||||
};
|
||||
}
|
||||
;
|
||||
|
||||
classDefStatement
|
||||
: classDef CLASSDEF_ID CLASSDEF_STYLEOPTS {
|
||||
|
@@ -65,6 +65,8 @@ export class StateDB {
|
||||
constructor(version) {
|
||||
this.clear();
|
||||
|
||||
this.links = new Map();
|
||||
|
||||
this.version = version;
|
||||
|
||||
// Needed for JISON since it only supports direct properties
|
||||
@@ -125,6 +127,11 @@ export class StateDB {
|
||||
* @type {number}
|
||||
*/
|
||||
dividerCnt = 0;
|
||||
/**
|
||||
* @private
|
||||
* @type {Map<string, { url: string, tooltip: string }>}
|
||||
*/
|
||||
links = new Map();
|
||||
|
||||
static relationType = {
|
||||
AGGREGATION: 0,
|
||||
@@ -278,6 +285,9 @@ export class StateDB {
|
||||
case STMT_APPLYCLASS:
|
||||
this.setCssClass(item.id.trim(), item.styleClass);
|
||||
break;
|
||||
case 'click':
|
||||
this.addLink(item.id, item.url, item.tooltip);
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
@@ -406,6 +416,7 @@ export class StateDB {
|
||||
this.startEndCount = 0;
|
||||
this.classes = newClassesList();
|
||||
if (!saveCommon) {
|
||||
this.links = new Map();
|
||||
commonClear();
|
||||
}
|
||||
}
|
||||
@@ -570,6 +581,25 @@ export class StateDB {
|
||||
return 'divider-id-' + this.dividerCnt;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a clickable link to a state.
|
||||
* @param {string} stateId
|
||||
* @param {string} url
|
||||
* @param {string} tooltip
|
||||
*/
|
||||
addLink(stateId, url, tooltip = '') {
|
||||
this.links.set(stateId, { url, tooltip });
|
||||
log.warn('Adding link', stateId, url, tooltip);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all registered links.
|
||||
* @returns {Map<string, { url: string, tooltip: string }>}
|
||||
*/
|
||||
getLinks() {
|
||||
return this.links;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the parser comes across a (style) class definition
|
||||
* @example classDef my-style fill:#f96;
|
||||
|
@@ -68,6 +68,61 @@ export const draw = async function (text: string, id: string, _version: string,
|
||||
// console.log('REF1:', data4Layout);
|
||||
await render(data4Layout, svg);
|
||||
const padding = 8;
|
||||
|
||||
// Inject clickable links after nodes are rendered
|
||||
try {
|
||||
const links: Map<string, { url: string; tooltip: string }> =
|
||||
typeof diag.db.getLinks === 'function' ? diag.db.getLinks() : new Map();
|
||||
|
||||
type StateKey = string | { id: string };
|
||||
|
||||
links.forEach((linkInfo, key: StateKey) => {
|
||||
const stateId = typeof key === 'string' ? key : typeof key?.id === 'string' ? key.id : '';
|
||||
|
||||
if (!stateId) {
|
||||
log.warn('⚠️ Invalid or missing stateId from key:', JSON.stringify(key));
|
||||
return;
|
||||
}
|
||||
|
||||
const allNodes = svg.node()?.querySelectorAll('g');
|
||||
let matchedElem: SVGGElement | undefined;
|
||||
|
||||
allNodes?.forEach((g: SVGGElement) => {
|
||||
const text = g.textContent?.trim();
|
||||
if (text === stateId) {
|
||||
matchedElem = g;
|
||||
}
|
||||
});
|
||||
|
||||
if (!matchedElem) {
|
||||
log.warn('⚠️ Could not find node matching text:', stateId);
|
||||
return;
|
||||
}
|
||||
|
||||
const parent = matchedElem.parentNode;
|
||||
if (!parent) {
|
||||
log.warn('⚠️ Node has no parent, cannot wrap:', stateId);
|
||||
return;
|
||||
}
|
||||
|
||||
const a = document.createElementNS('http://www.w3.org/2000/svg', 'a');
|
||||
const cleanedUrl = linkInfo.url.replace(/^"+|"+$/g, ''); // remove leading/trailing quotes
|
||||
a.setAttributeNS('http://www.w3.org/1999/xlink', 'xlink:href', cleanedUrl);
|
||||
a.setAttribute('target', '_blank');
|
||||
if (linkInfo.tooltip) {
|
||||
const tooltip = linkInfo.tooltip.replace(/^"+|"+$/g, '');
|
||||
a.setAttribute('title', tooltip);
|
||||
}
|
||||
|
||||
parent.replaceChild(a, matchedElem);
|
||||
a.appendChild(matchedElem);
|
||||
|
||||
log.info('🔗 Wrapped node in <a> tag for:', stateId, linkInfo.url);
|
||||
});
|
||||
} catch (err) {
|
||||
log.error('❌ Error injecting clickable links:', err);
|
||||
}
|
||||
|
||||
utils.insertTitle(
|
||||
svg,
|
||||
'statediagramTitleText',
|
||||
|
Reference in New Issue
Block a user