Add support for collapsing blocks [#46]
This commit is contained in:
parent
46fdca1599
commit
e22381e37d
|
@ -2736,6 +2736,7 @@ define('sequence/Generator',['core/ArrayUtilities'], (array) => {
|
||||||
blockType,
|
blockType,
|
||||||
tag: this.textFormatter(tag),
|
tag: this.textFormatter(tag),
|
||||||
label: this.textFormatter(label),
|
label: this.textFormatter(label),
|
||||||
|
canHide: true,
|
||||||
left: leftGAgent.id,
|
left: leftGAgent.id,
|
||||||
right: rightGAgent.id,
|
right: rightGAgent.id,
|
||||||
ln,
|
ln,
|
||||||
|
@ -2881,6 +2882,7 @@ define('sequence/Generator',['core/ArrayUtilities'], (array) => {
|
||||||
type: 'block begin',
|
type: 'block begin',
|
||||||
blockType,
|
blockType,
|
||||||
tag: this.textFormatter(tag),
|
tag: this.textFormatter(tag),
|
||||||
|
canHide: false,
|
||||||
label: this.textFormatter(label),
|
label: this.textFormatter(label),
|
||||||
left: details.leftGAgent.id,
|
left: details.leftGAgent.id,
|
||||||
right: details.rightGAgent.id,
|
right: details.rightGAgent.id,
|
||||||
|
@ -3898,6 +3900,7 @@ define('sequence/components/BaseComponent',[],() => {
|
||||||
}
|
}
|
||||||
|
|
||||||
separationPre(/*stage, {
|
separationPre(/*stage, {
|
||||||
|
renderer,
|
||||||
theme,
|
theme,
|
||||||
agentInfos,
|
agentInfos,
|
||||||
visibleAgentIDs,
|
visibleAgentIDs,
|
||||||
|
@ -3911,6 +3914,7 @@ define('sequence/components/BaseComponent',[],() => {
|
||||||
}
|
}
|
||||||
|
|
||||||
separation(/*stage, {
|
separation(/*stage, {
|
||||||
|
renderer,
|
||||||
theme,
|
theme,
|
||||||
agentInfos,
|
agentInfos,
|
||||||
visibleAgentIDs,
|
visibleAgentIDs,
|
||||||
|
@ -3924,6 +3928,7 @@ define('sequence/components/BaseComponent',[],() => {
|
||||||
}
|
}
|
||||||
|
|
||||||
renderPre(/*stage, {
|
renderPre(/*stage, {
|
||||||
|
renderer,
|
||||||
theme,
|
theme,
|
||||||
agentInfos,
|
agentInfos,
|
||||||
textSizer,
|
textSizer,
|
||||||
|
@ -3934,6 +3939,7 @@ define('sequence/components/BaseComponent',[],() => {
|
||||||
}
|
}
|
||||||
|
|
||||||
render(/*stage, {
|
render(/*stage, {
|
||||||
|
renderer,
|
||||||
topY,
|
topY,
|
||||||
primaryY,
|
primaryY,
|
||||||
fillLayer,
|
fillLayer,
|
||||||
|
@ -3949,6 +3955,22 @@ define('sequence/components/BaseComponent',[],() => {
|
||||||
}*/) {
|
}*/) {
|
||||||
// return bottom Y coordinate
|
// return bottom Y coordinate
|
||||||
}
|
}
|
||||||
|
|
||||||
|
renderHidden(/*stage, {
|
||||||
|
(same args as render, with primaryY = topY)
|
||||||
|
}*/) {
|
||||||
|
}
|
||||||
|
|
||||||
|
shouldHide(/*stage, {
|
||||||
|
renderer,
|
||||||
|
theme,
|
||||||
|
agentInfos,
|
||||||
|
textSizer,
|
||||||
|
state,
|
||||||
|
components,
|
||||||
|
}*/) {
|
||||||
|
// return {self, nest}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
BaseComponent.cleanRenderPreResult = ({
|
BaseComponent.cleanRenderPreResult = ({
|
||||||
|
@ -4070,6 +4092,12 @@ define('sequence/components/Block',[
|
||||||
'x2': agentInfoR.x,
|
'x2': agentInfoR.x,
|
||||||
'y2': y,
|
'y2': y,
|
||||||
}));
|
}));
|
||||||
|
} else if(blockInfo.canHide) {
|
||||||
|
clickable.setAttribute(
|
||||||
|
'class',
|
||||||
|
clickable.getAttribute('class') +
|
||||||
|
(blockInfo.hide ? ' collapsed' : ' expanded')
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return y + labelHeight + config.section.padding.top;
|
return y + labelHeight + config.section.padding.top;
|
||||||
|
@ -4086,8 +4114,11 @@ define('sequence/components/Block',[
|
||||||
}
|
}
|
||||||
|
|
||||||
storeBlockInfo(stage, env) {
|
storeBlockInfo(stage, env) {
|
||||||
|
const canHide = stage.canHide;
|
||||||
const blockInfo = {
|
const blockInfo = {
|
||||||
type: stage.blockType,
|
type: stage.blockType,
|
||||||
|
canHide,
|
||||||
|
hide: canHide && env.renderer.isCollapsed(stage.ln),
|
||||||
hold: null,
|
hold: null,
|
||||||
startY: null,
|
startY: null,
|
||||||
};
|
};
|
||||||
|
@ -4125,6 +4156,14 @@ define('sequence/components/Block',[
|
||||||
|
|
||||||
return super.render(stage, env, true);
|
return super.render(stage, env, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
shouldHide({left}, env) {
|
||||||
|
const blockInfo = env.state.blocks.get(left);
|
||||||
|
return {
|
||||||
|
self: false,
|
||||||
|
nest: blockInfo.hide ? 1 : 0,
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class BlockEnd extends BaseComponent {
|
class BlockEnd extends BaseComponent {
|
||||||
|
@ -4148,7 +4187,12 @@ define('sequence/components/Block',[
|
||||||
const agentInfoL = env.agentInfos.get(left);
|
const agentInfoL = env.agentInfos.get(left);
|
||||||
const agentInfoR = env.agentInfos.get(right);
|
const agentInfoR = env.agentInfos.get(right);
|
||||||
|
|
||||||
let shapes = config.boxRenderer({
|
let renderFn = config.boxRenderer;
|
||||||
|
if(blockInfo.hide) {
|
||||||
|
renderFn = config.collapsedBoxRenderer || renderFn;
|
||||||
|
}
|
||||||
|
|
||||||
|
let shapes = renderFn({
|
||||||
x: agentInfoL.x,
|
x: agentInfoL.x,
|
||||||
y: blockInfo.startY,
|
y: blockInfo.startY,
|
||||||
width: agentInfoR.x - agentInfoL.x,
|
width: agentInfoR.x - agentInfoL.x,
|
||||||
|
@ -4168,6 +4212,14 @@ define('sequence/components/Block',[
|
||||||
|
|
||||||
return env.primaryY + config.margin.bottom + env.theme.actionMargin;
|
return env.primaryY + config.margin.bottom + env.theme.actionMargin;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
shouldHide({left}, env) {
|
||||||
|
const blockInfo = env.state.blocks.get(left);
|
||||||
|
return {
|
||||||
|
self: false,
|
||||||
|
nest: blockInfo.hide ? -1 : 0,
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
BaseComponent.register('block begin', new BlockBegin());
|
BaseComponent.register('block begin', new BlockBegin());
|
||||||
|
@ -4254,6 +4306,27 @@ define('sequence/components/Parallel',[
|
||||||
env.makeRegion = originalMakeRegion;
|
env.makeRegion = originalMakeRegion;
|
||||||
return bottomY;
|
return bottomY;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
renderHidden(stage, env) {
|
||||||
|
stage.stages.forEach((subStage) => {
|
||||||
|
const component = env.components.get(subStage.type);
|
||||||
|
component.renderHidden(subStage, env);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
shouldHide(stage, env) {
|
||||||
|
const result = {
|
||||||
|
self: false,
|
||||||
|
nest: 0,
|
||||||
|
};
|
||||||
|
stage.stages.forEach((subStage) => {
|
||||||
|
const component = env.components.get(subStage.type);
|
||||||
|
const hide = component.shouldHide(subStage, env) || {};
|
||||||
|
result.self = (result.self || Boolean(hide.self));
|
||||||
|
result.nest += (hide.nest || 0);
|
||||||
|
});
|
||||||
|
return result;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
BaseComponent.register('parallel', new Parallel());
|
BaseComponent.register('parallel', new Parallel());
|
||||||
|
@ -4276,6 +4349,10 @@ define('sequence/components/Marker',['./BaseComponent'], (BaseComponent) => {
|
||||||
render({name}, {topY, state}) {
|
render({name}, {topY, state}) {
|
||||||
state.marks.set(name, topY);
|
state.marks.set(name, topY);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
renderHidden(stage, env) {
|
||||||
|
this.render(stage, env);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class Async extends BaseComponent {
|
class Async extends BaseComponent {
|
||||||
|
@ -4633,6 +4710,12 @@ define('sequence/components/AgentCap',[
|
||||||
});
|
});
|
||||||
return maxEnd + env.theme.actionMargin;
|
return maxEnd + env.theme.actionMargin;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
renderHidden({agentIDs}, env) {
|
||||||
|
agentIDs.forEach((id) => {
|
||||||
|
env.drawAgentLine(id, env.topY, !this.begin);
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
BaseComponent.register('agent begin', new AgentCap(true));
|
BaseComponent.register('agent begin', new AgentCap(true));
|
||||||
|
@ -4674,6 +4757,10 @@ define('sequence/components/AgentHighlight',['./BaseComponent'], (BaseComponent)
|
||||||
});
|
});
|
||||||
return env.primaryY + env.theme.actionMargin;
|
return env.primaryY + env.theme.actionMargin;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
renderHidden(stage, env) {
|
||||||
|
this.render(stage, env);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
BaseComponent.register('agent highlight', new AgentHighlight());
|
BaseComponent.register('agent highlight', new AgentHighlight());
|
||||||
|
@ -5164,6 +5251,10 @@ define('sequence/components/Connect',[
|
||||||
});
|
});
|
||||||
return env.primaryY + env.theme.actionMargin;
|
return env.primaryY + env.theme.actionMargin;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
renderHidden(stage, env) {
|
||||||
|
this.render(stage, env);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class ConnectDelayEnd extends Connect {
|
class ConnectDelayEnd extends Connect {
|
||||||
|
@ -5669,6 +5760,7 @@ define('sequence/Renderer',[
|
||||||
this.knownThemeDefs = new Set();
|
this.knownThemeDefs = new Set();
|
||||||
this.knownDefs = new Set();
|
this.knownDefs = new Set();
|
||||||
this.highlights = new Map();
|
this.highlights = new Map();
|
||||||
|
this.collapsed = new Set();
|
||||||
this.currentHighlight = -1;
|
this.currentHighlight = -1;
|
||||||
this.buildStaticElements();
|
this.buildStaticElements();
|
||||||
this.components.forEach((component) => {
|
this.components.forEach((component) => {
|
||||||
|
@ -5679,7 +5771,6 @@ define('sequence/Renderer',[
|
||||||
_bindMethods() {
|
_bindMethods() {
|
||||||
this.separationStage = this.separationStage.bind(this);
|
this.separationStage = this.separationStage.bind(this);
|
||||||
this.renderStage = this.renderStage.bind(this);
|
this.renderStage = this.renderStage.bind(this);
|
||||||
this.addSeparation = this.addSeparation.bind(this);
|
|
||||||
this.addThemeDef = this.addThemeDef.bind(this);
|
this.addThemeDef = this.addThemeDef.bind(this);
|
||||||
this.addDef = this.addDef.bind(this);
|
this.addDef = this.addDef.bind(this);
|
||||||
}
|
}
|
||||||
|
@ -5775,9 +5866,37 @@ define('sequence/Renderer',[
|
||||||
info2.separations.set(agentID1, Math.max(d2, dist));
|
info2.separations.set(agentID1, Math.max(d2, dist));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
checkHidden(stage) {
|
||||||
|
const component = this.components.get(stage.type);
|
||||||
|
const env = {
|
||||||
|
renderer: this,
|
||||||
|
theme: this.theme,
|
||||||
|
agentInfos: this.agentInfos,
|
||||||
|
textSizer: this.sizer,
|
||||||
|
state: this.state,
|
||||||
|
components: this.components,
|
||||||
|
};
|
||||||
|
|
||||||
|
const hide = component.shouldHide(stage, env) || {};
|
||||||
|
|
||||||
|
const wasHidden = (this.hideNest > 0);
|
||||||
|
this.hideNest += hide.nest || 0;
|
||||||
|
const isHidden = (this.hideNest > 0);
|
||||||
|
|
||||||
|
if(this.hideNest < 0) {
|
||||||
|
throw new Error('Unexpected nesting in ' + stage.type);
|
||||||
|
}
|
||||||
|
if(wasHidden === isHidden) {
|
||||||
|
return isHidden;
|
||||||
|
} else {
|
||||||
|
return Boolean(hide.self);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
separationStage(stage) {
|
separationStage(stage) {
|
||||||
const agentSpaces = new Map();
|
const agentSpaces = new Map();
|
||||||
const agentIDs = this.visibleAgentIDs.slice();
|
const agentIDs = this.visibleAgentIDs.slice();
|
||||||
|
const seps = [];
|
||||||
|
|
||||||
const addSpacing = (agentID, {left, right}) => {
|
const addSpacing = (agentID, {left, right}) => {
|
||||||
const current = agentSpaces.get(agentID);
|
const current = agentSpaces.get(agentID);
|
||||||
|
@ -5785,30 +5904,47 @@ define('sequence/Renderer',[
|
||||||
current.right = Math.max(current.right, right);
|
current.right = Math.max(current.right, right);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const addSeparation = (agentID1, agentID2, dist) => {
|
||||||
|
seps.push({agentID1, agentID2, dist});
|
||||||
|
};
|
||||||
|
|
||||||
this.agentInfos.forEach((agentInfo) => {
|
this.agentInfos.forEach((agentInfo) => {
|
||||||
const rad = agentInfo.currentRad;
|
const rad = agentInfo.currentRad;
|
||||||
agentInfo.currentMaxRad = rad;
|
agentInfo.currentMaxRad = rad;
|
||||||
agentSpaces.set(agentInfo.id, {left: rad, right: rad});
|
agentSpaces.set(agentInfo.id, {left: rad, right: rad});
|
||||||
});
|
});
|
||||||
|
|
||||||
const env = {
|
const env = {
|
||||||
|
renderer: this,
|
||||||
theme: this.theme,
|
theme: this.theme,
|
||||||
agentInfos: this.agentInfos,
|
agentInfos: this.agentInfos,
|
||||||
visibleAgentIDs: this.visibleAgentIDs,
|
visibleAgentIDs: this.visibleAgentIDs,
|
||||||
momentaryAgentIDs: agentIDs,
|
momentaryAgentIDs: agentIDs,
|
||||||
textSizer: this.sizer,
|
textSizer: this.sizer,
|
||||||
addSpacing,
|
addSpacing,
|
||||||
addSeparation: this.addSeparation,
|
addSeparation,
|
||||||
state: this.state,
|
state: this.state,
|
||||||
components: this.components,
|
components: this.components,
|
||||||
};
|
};
|
||||||
|
|
||||||
const component = this.components.get(stage.type);
|
const component = this.components.get(stage.type);
|
||||||
if(!component) {
|
if(!component) {
|
||||||
throw new Error('Unknown component: ' + stage.type);
|
throw new Error('Unknown component: ' + stage.type);
|
||||||
}
|
}
|
||||||
|
|
||||||
component.separationPre(stage, env);
|
component.separationPre(stage, env);
|
||||||
component.separation(stage, env);
|
component.separation(stage, env);
|
||||||
|
|
||||||
|
if(this.checkHidden(stage)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
array.mergeSets(agentIDs, this.visibleAgentIDs);
|
array.mergeSets(agentIDs, this.visibleAgentIDs);
|
||||||
|
|
||||||
|
seps.forEach(({agentID1, agentID2, dist}) => {
|
||||||
|
this.addSeparation(agentID1, agentID2, dist);
|
||||||
|
});
|
||||||
|
|
||||||
agentIDs.forEach((agentIDR) => {
|
agentIDs.forEach((agentIDR) => {
|
||||||
const infoR = this.agentInfos.get(agentIDR);
|
const infoR = this.agentInfos.get(agentIDR);
|
||||||
const sepR = agentSpaces.get(agentIDR);
|
const sepR = agentSpaces.get(agentIDR);
|
||||||
|
@ -5885,6 +6021,13 @@ define('sequence/Renderer',[
|
||||||
list.push(o);
|
list.push(o);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
forwardEvent(source, sourceEvent, forwardEvent, forwardArgs) {
|
||||||
|
source.addEventListener(
|
||||||
|
sourceEvent,
|
||||||
|
this.trigger.bind(this, forwardEvent, forwardArgs)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
renderStage(stage) {
|
renderStage(stage) {
|
||||||
this.agentInfos.forEach((agentInfo) => {
|
this.agentInfos.forEach((agentInfo) => {
|
||||||
const rad = agentInfo.currentRad;
|
const rad = agentInfo.currentRad;
|
||||||
|
@ -5892,6 +6035,7 @@ define('sequence/Renderer',[
|
||||||
});
|
});
|
||||||
|
|
||||||
const envPre = {
|
const envPre = {
|
||||||
|
renderer: this,
|
||||||
theme: this.theme,
|
theme: this.theme,
|
||||||
agentInfos: this.agentInfos,
|
agentInfos: this.agentInfos,
|
||||||
textSizer: this.sizer,
|
textSizer: this.sizer,
|
||||||
|
@ -5905,10 +6049,6 @@ define('sequence/Renderer',[
|
||||||
|
|
||||||
const topY = this.checkAgentRange(agentIDs, asynchronousY);
|
const topY = this.checkAgentRange(agentIDs, asynchronousY);
|
||||||
|
|
||||||
const eventOut = () => {
|
|
||||||
this.trigger('mouseout');
|
|
||||||
};
|
|
||||||
|
|
||||||
const makeRegion = ({
|
const makeRegion = ({
|
||||||
stageOverride = null,
|
stageOverride = null,
|
||||||
unmasked = false,
|
unmasked = false,
|
||||||
|
@ -5917,18 +6057,16 @@ define('sequence/Renderer',[
|
||||||
const targetStage = (stageOverride || stage);
|
const targetStage = (stageOverride || stage);
|
||||||
this.addHighlightObject(targetStage.ln, o);
|
this.addHighlightObject(targetStage.ln, o);
|
||||||
o.setAttribute('class', 'region');
|
o.setAttribute('class', 'region');
|
||||||
o.addEventListener('mouseenter', () => {
|
this.forwardEvent(o, 'mouseenter', 'mouseover', [targetStage]);
|
||||||
this.trigger('mouseover', [targetStage]);
|
this.forwardEvent(o, 'mouseleave', 'mouseout', [targetStage]);
|
||||||
});
|
this.forwardEvent(o, 'click', 'click', [targetStage]);
|
||||||
o.addEventListener('mouseleave', eventOut);
|
this.forwardEvent(o, 'dblclick', 'dblclick', [targetStage]);
|
||||||
o.addEventListener('click', () => {
|
|
||||||
this.trigger('click', [targetStage]);
|
|
||||||
});
|
|
||||||
(unmasked ? this.unmaskedShapes : this.shapes).appendChild(o);
|
(unmasked ? this.unmaskedShapes : this.shapes).appendChild(o);
|
||||||
return o;
|
return o;
|
||||||
};
|
};
|
||||||
|
|
||||||
const env = {
|
const env = {
|
||||||
|
renderer: this,
|
||||||
topY,
|
topY,
|
||||||
primaryY: topY + topShift,
|
primaryY: topY + topShift,
|
||||||
fillLayer: this.backgroundFills,
|
fillLayer: this.backgroundFills,
|
||||||
|
@ -5950,9 +6088,15 @@ define('sequence/Renderer',[
|
||||||
components: this.components,
|
components: this.components,
|
||||||
};
|
};
|
||||||
|
|
||||||
const bottomY = Math.max(topY, component.render(stage, env) || 0);
|
let bottomY = topY;
|
||||||
this.markAgentRange(agentIDs, bottomY);
|
if(this.checkHidden(stage)) {
|
||||||
|
env.primaryY = topY;
|
||||||
|
component.renderHidden(stage, env);
|
||||||
|
} else {
|
||||||
|
bottomY = Math.max(bottomY, component.render(stage, env) || 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.markAgentRange(agentIDs, bottomY);
|
||||||
this.currentY = bottomY;
|
this.currentY = bottomY;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6057,6 +6201,7 @@ define('sequence/Renderer',[
|
||||||
component.resetState(this.state);
|
component.resetState(this.state);
|
||||||
});
|
});
|
||||||
this.currentY = 0;
|
this.currentY = 0;
|
||||||
|
this.hideNest = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
_reset(theme) {
|
_reset(theme) {
|
||||||
|
@ -6103,6 +6248,49 @@ define('sequence/Renderer',[
|
||||||
this.currentHighlight = line;
|
this.currentHighlight = line;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
isCollapsed(line) {
|
||||||
|
return this.collapsed.has(line);
|
||||||
|
}
|
||||||
|
|
||||||
|
setCollapseAll(collapsed) {
|
||||||
|
if(collapsed) {
|
||||||
|
throw new Error('Cannot collapse all');
|
||||||
|
} else {
|
||||||
|
if(this.collapsed.size === 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
this.collapsed.clear();
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
_setCollapsed(line, collapsed) {
|
||||||
|
if(typeof line !== 'number') {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if(collapsed === this.isCollapsed(line)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if(collapsed) {
|
||||||
|
this.collapsed.add(line);
|
||||||
|
} else {
|
||||||
|
this.collapsed.delete(line);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
setCollapsed(line, collapsed = true) {
|
||||||
|
if(line === null) {
|
||||||
|
return this.setCollapseAll(collapsed);
|
||||||
|
}
|
||||||
|
if(Array.isArray(line)) {
|
||||||
|
return line
|
||||||
|
.map((ln) => this._setCollapsed(ln, collapsed))
|
||||||
|
.some((changed) => changed);
|
||||||
|
}
|
||||||
|
return this._setCollapsed(line, collapsed);
|
||||||
|
}
|
||||||
|
|
||||||
render(sequence) {
|
render(sequence) {
|
||||||
const prevHighlight = this.currentHighlight;
|
const prevHighlight = this.currentHighlight;
|
||||||
const oldTheme = this.theme;
|
const oldTheme = this.theme;
|
||||||
|
@ -7143,6 +7331,13 @@ define('sequence/themes/Basic',[
|
||||||
'rx': 2,
|
'rx': 2,
|
||||||
'ry': 2,
|
'ry': 2,
|
||||||
}),
|
}),
|
||||||
|
collapsedBoxRenderer: BaseTheme.renderRef.bind(null, {
|
||||||
|
'fill': '#FFFFFF',
|
||||||
|
'stroke': '#000000',
|
||||||
|
'stroke-width': 1.5,
|
||||||
|
'rx': 2,
|
||||||
|
'ry': 2,
|
||||||
|
}),
|
||||||
section: SHARED_BLOCK_SECTION,
|
section: SHARED_BLOCK_SECTION,
|
||||||
sepRenderer: SVGShapes.renderLine.bind(null, {
|
sepRenderer: SVGShapes.renderLine.bind(null, {
|
||||||
'stroke': '#000000',
|
'stroke': '#000000',
|
||||||
|
@ -7527,6 +7722,11 @@ define('sequence/themes/Monospace',[
|
||||||
'stroke': '#000000',
|
'stroke': '#000000',
|
||||||
'stroke-width': 2,
|
'stroke-width': 2,
|
||||||
}),
|
}),
|
||||||
|
collapsedBoxRenderer: BaseTheme.renderRef.bind(null, {
|
||||||
|
'fill': '#FFFFFF',
|
||||||
|
'stroke': '#000000',
|
||||||
|
'stroke-width': 2,
|
||||||
|
}),
|
||||||
section: SHARED_BLOCK_SECTION,
|
section: SHARED_BLOCK_SECTION,
|
||||||
sepRenderer: SVGShapes.renderLine.bind(null, {
|
sepRenderer: SVGShapes.renderLine.bind(null, {
|
||||||
'stroke': '#000000',
|
'stroke': '#000000',
|
||||||
|
@ -7919,6 +8119,13 @@ define('sequence/themes/Chunky',[
|
||||||
'rx': 5,
|
'rx': 5,
|
||||||
'ry': 5,
|
'ry': 5,
|
||||||
}),
|
}),
|
||||||
|
collapsedBoxRenderer: BaseTheme.renderRef.bind(null, {
|
||||||
|
'fill': '#FFFFFF',
|
||||||
|
'stroke': '#000000',
|
||||||
|
'stroke-width': 4,
|
||||||
|
'rx': 5,
|
||||||
|
'ry': 5,
|
||||||
|
}),
|
||||||
section: SHARED_BLOCK_SECTION,
|
section: SHARED_BLOCK_SECTION,
|
||||||
sepRenderer: SVGShapes.renderLine.bind(null, {
|
sepRenderer: SVGShapes.renderLine.bind(null, {
|
||||||
'stroke': '#000000',
|
'stroke': '#000000',
|
||||||
|
@ -8712,6 +8919,7 @@ define('sequence/themes/Sketch',[
|
||||||
bottom: 0,
|
bottom: 0,
|
||||||
},
|
},
|
||||||
boxRenderer: null,
|
boxRenderer: null,
|
||||||
|
collapsedBoxRenderer: null,
|
||||||
section: SHARED_BLOCK_SECTION,
|
section: SHARED_BLOCK_SECTION,
|
||||||
sepRenderer: null,
|
sepRenderer: null,
|
||||||
},
|
},
|
||||||
|
@ -8921,6 +9129,8 @@ define('sequence/themes/Sketch',[
|
||||||
|
|
||||||
this.blocks.ref.boxRenderer = this.renderRefBlock.bind(this);
|
this.blocks.ref.boxRenderer = this.renderRefBlock.bind(this);
|
||||||
this.blocks[''].boxRenderer = this.renderBlock.bind(this);
|
this.blocks[''].boxRenderer = this.renderBlock.bind(this);
|
||||||
|
this.blocks[''].collapsedBoxRenderer =
|
||||||
|
this.renderCollapsedBlock.bind(this);
|
||||||
this.blocks.ref.section.tag.boxRenderer = this.renderTag;
|
this.blocks.ref.section.tag.boxRenderer = this.renderTag;
|
||||||
this.blocks[''].section.tag.boxRenderer = this.renderTag;
|
this.blocks[''].section.tag.boxRenderer = this.renderTag;
|
||||||
this.blocks[''].sepRenderer = this.renderSeparator.bind(this);
|
this.blocks[''].sepRenderer = this.renderSeparator.bind(this);
|
||||||
|
@ -9318,6 +9528,10 @@ define('sequence/themes/Sketch',[
|
||||||
return this.renderBox(position, {fill: 'none', thick: true});
|
return this.renderBox(position, {fill: 'none', thick: true});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
renderCollapsedBlock(position) {
|
||||||
|
return this.renderRefBlock(position);
|
||||||
|
}
|
||||||
|
|
||||||
renderTag({x, y, width, height}) {
|
renderTag({x, y, width, height}) {
|
||||||
const x2 = x + width;
|
const x2 = x + width;
|
||||||
const y2 = y + height;
|
const y2 = y + height;
|
||||||
|
@ -9486,9 +9700,14 @@ define('sequence/SequenceDiagram',[
|
||||||
this.renderer = new Renderer(Object.assign({themes}, options));
|
this.renderer = new Renderer(Object.assign({themes}, options));
|
||||||
this.exporter = new Exporter();
|
this.exporter = new Exporter();
|
||||||
this.renderer.addEventForwarding(this);
|
this.renderer.addEventForwarding(this);
|
||||||
|
this.latestProcessed = null;
|
||||||
|
this.isInteractive = false;
|
||||||
if(options.container) {
|
if(options.container) {
|
||||||
options.container.appendChild(this.dom());
|
options.container.appendChild(this.dom());
|
||||||
}
|
}
|
||||||
|
if(options.interactive) {
|
||||||
|
this.addInteractivity();
|
||||||
|
}
|
||||||
if(typeof this.code === 'string') {
|
if(typeof this.code === 'string') {
|
||||||
this.render();
|
this.render();
|
||||||
}
|
}
|
||||||
|
@ -9501,6 +9720,7 @@ define('sequence/SequenceDiagram',[
|
||||||
themes: this.renderer.getThemes(),
|
themes: this.renderer.getThemes(),
|
||||||
namespace: null,
|
namespace: null,
|
||||||
components: this.renderer.components,
|
components: this.renderer.components,
|
||||||
|
interactive: this.isInteractive,
|
||||||
SVGTextBlockClass: this.renderer.SVGTextBlockClass,
|
SVGTextBlockClass: this.renderer.SVGTextBlockClass,
|
||||||
}, options));
|
}, options));
|
||||||
}
|
}
|
||||||
|
@ -9523,10 +9743,40 @@ define('sequence/SequenceDiagram',[
|
||||||
this.renderer.addTheme(theme);
|
this.renderer.addTheme(theme);
|
||||||
}
|
}
|
||||||
|
|
||||||
setHighlight(line = null) {
|
setHighlight(line) {
|
||||||
this.renderer.setHighlight(line);
|
this.renderer.setHighlight(line);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
isCollapsed(line) {
|
||||||
|
return this.renderer.isCollapsed(line);
|
||||||
|
}
|
||||||
|
|
||||||
|
setCollapsed(line, collapsed = true, {render = true} = {}) {
|
||||||
|
if(!this.renderer.setCollapsed(line, collapsed)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if(render && this.latestProcessed) {
|
||||||
|
this.render(this.latestProcessed);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
collapse(line, options) {
|
||||||
|
return this.setCollapsed(line, true, options);
|
||||||
|
}
|
||||||
|
|
||||||
|
expand(line, options) {
|
||||||
|
return this.setCollapsed(line, false, options);
|
||||||
|
}
|
||||||
|
|
||||||
|
toggleCollapsed(line, options) {
|
||||||
|
return this.setCollapsed(line, !this.isCollapsed(line), options);
|
||||||
|
}
|
||||||
|
|
||||||
|
expandAll(options) {
|
||||||
|
return this.setCollapsed(null, false, options);
|
||||||
|
}
|
||||||
|
|
||||||
getThemeNames() {
|
getThemeNames() {
|
||||||
return this.renderer.getThemeNames();
|
return this.renderer.getThemeNames();
|
||||||
}
|
}
|
||||||
|
@ -9593,6 +9843,8 @@ define('sequence/SequenceDiagram',[
|
||||||
processed = this.process(this.code);
|
processed = this.process(this.code);
|
||||||
}
|
}
|
||||||
this.renderer.render(processed);
|
this.renderer.render(processed);
|
||||||
|
this.latestProcessed = processed;
|
||||||
|
this.trigger('render', [this]);
|
||||||
} finally {
|
} finally {
|
||||||
if(dom.parentNode !== originalParent) {
|
if(dom.parentNode !== originalParent) {
|
||||||
document.body.removeChild(dom);
|
document.body.removeChild(dom);
|
||||||
|
@ -9613,6 +9865,17 @@ define('sequence/SequenceDiagram',[
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
addInteractivity() {
|
||||||
|
if(this.isInteractive) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.isInteractive = true;
|
||||||
|
|
||||||
|
this.addEventListener('click', (element) => {
|
||||||
|
this.toggleCollapsed(element.ln);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
extractCodeFromSVG(svg) {
|
extractCodeFromSVG(svg) {
|
||||||
return extractCodeFromSVG(svg);
|
return extractCodeFromSVG(svg);
|
||||||
}
|
}
|
||||||
|
@ -9622,6 +9885,17 @@ define('sequence/SequenceDiagram',[
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function datasetBoolean(value) {
|
||||||
|
return value !== undefined && value !== 'false';
|
||||||
|
}
|
||||||
|
|
||||||
|
function parseTagOptions(element) {
|
||||||
|
return {
|
||||||
|
namespace: element.dataset.sdNamespace || null,
|
||||||
|
interactive: datasetBoolean(element.dataset.sdInteractive),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
function convert(element, code = null, options = {}) {
|
function convert(element, code = null, options = {}) {
|
||||||
if(element.tagName === 'svg') {
|
if(element.tagName === 'svg') {
|
||||||
return null;
|
return null;
|
||||||
|
@ -9633,7 +9907,13 @@ define('sequence/SequenceDiagram',[
|
||||||
options = code;
|
options = code;
|
||||||
code = options.code;
|
code = options.code;
|
||||||
}
|
}
|
||||||
const diagram = new SequenceDiagram(code, options);
|
|
||||||
|
const tagOptions = parseTagOptions(element);
|
||||||
|
|
||||||
|
const diagram = new SequenceDiagram(
|
||||||
|
code,
|
||||||
|
Object.assign(tagOptions, options)
|
||||||
|
);
|
||||||
const newElement = diagram.dom();
|
const newElement = diagram.dom();
|
||||||
element.parentNode.insertBefore(newElement, element);
|
element.parentNode.insertBefore(newElement, element);
|
||||||
element.parentNode.removeChild(element);
|
element.parentNode.removeChild(element);
|
||||||
|
|
File diff suppressed because one or more lines are too long
156
library.htm
156
library.htm
|
@ -409,10 +409,17 @@ diagram = new SequenceDiagram();
|
||||||
Creates a new SequenceDiagram object. Options is an object which can contain:
|
Creates a new SequenceDiagram object. Options is an object which can contain:
|
||||||
</p>
|
</p>
|
||||||
<ul>
|
<ul>
|
||||||
<li><code>code</code>: Alternative way of specifying code, instead of using a separate argument.</li>
|
<li><code>code</code>: Alternative way of specifying code, instead of using a
|
||||||
<li><code>container</code>: DOM node to append the diagram to (defaults to null).</li>
|
separate argument.</li>
|
||||||
<li><code>themes</code>: List of themes to make available to the diagram (defaults to globally registered themes).</li>
|
<li><code>container</code>: DOM node to append the diagram to (defaults to
|
||||||
<li><code>namespace</code>: Each diagram on a page must have a unique namespace. By default a unique namespace is generated, but if you want something specific, enter it here.</li>
|
null).</li>
|
||||||
|
<li><code>themes</code>: List of themes to make available to the diagram
|
||||||
|
(defaults to globally registered themes).</li>
|
||||||
|
<li><code>namespace</code>: Each diagram on a page must have a unique namespace.
|
||||||
|
By default a unique namespace is generated, but if you want something specific,
|
||||||
|
enter it here.</li>
|
||||||
|
<li><code>interactive</code>: If <code>true</code>, will automatically call
|
||||||
|
<code>addInteractivity</code> when constructing the diagram.</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<h3 id="API_clone">.clone</h3>
|
<h3 id="API_clone">.clone</h3>
|
||||||
|
@ -503,6 +510,58 @@ themes = diagram.getThemes();
|
||||||
Returns a list of themes which are available to this diagram.
|
Returns a list of themes which are available to this diagram.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
<h3 id="API_addInteractivity">.addInteractivity</h3>
|
||||||
|
|
||||||
|
<div class="right">
|
||||||
|
<pre class="sequence-diagram" data-lang="sequence" data-sd-interactive>
|
||||||
|
begin A, B
|
||||||
|
if bored
|
||||||
|
A -> +B
|
||||||
|
-B --> A
|
||||||
|
end
|
||||||
|
if still bored
|
||||||
|
A -> +B
|
||||||
|
-B --> A
|
||||||
|
end
|
||||||
|
</pre>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<pre data-lang="javascript">
|
||||||
|
diagram.addInteractivity();
|
||||||
|
</pre>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
Makes the rendered diagram interactive. Currently this means adding a click
|
||||||
|
listener to any groups which causes them to collapse / expand. Try clicking on
|
||||||
|
the example to the right.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>The example here has CSS styling applied:</p>
|
||||||
|
|
||||||
|
<pre data-lang="css">
|
||||||
|
.region.collapsed,
|
||||||
|
.region.expanded {
|
||||||
|
cursor: pointer;
|
||||||
|
user-select: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.region.collapsed:hover .outline,
|
||||||
|
.region.expanded:hover .outline {
|
||||||
|
fill: rgba(255, 128, 0, 0.5);
|
||||||
|
}
|
||||||
|
</pre>
|
||||||
|
|
||||||
|
<p>It is also possible to enable interactivity using a HTML attribute:</p>
|
||||||
|
|
||||||
|
<pre data-lang="text/html">
|
||||||
|
<pre class="sequence-diagram" data-sd-interactive>
|
||||||
|
A -> +B
|
||||||
|
if something
|
||||||
|
-B --> A
|
||||||
|
end
|
||||||
|
</pre>
|
||||||
|
</pre>
|
||||||
|
|
||||||
<h3 id="API_getSVGSynchronous">.getSVGSynchronous</h3>
|
<h3 id="API_getSVGSynchronous">.getSVGSynchronous</h3>
|
||||||
|
|
||||||
<pre data-lang="javascript">
|
<pre data-lang="javascript">
|
||||||
|
@ -608,6 +667,87 @@ example:
|
||||||
}
|
}
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
|
<h3 id="API_setCollapsed">.setCollapsed</h3>
|
||||||
|
|
||||||
|
<pre data-lang="javascript">
|
||||||
|
diagram.setCollapsed(line, collapsed, options);
|
||||||
|
diagram.setCollapsed(line, collapsed);
|
||||||
|
</pre>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
Marks the given line as collapsed or non-collapsed. If an element defined at
|
||||||
|
that line can be collapsed, it will be modified during the next render. Returns
|
||||||
|
true if a change occurred, or false if the line already had the requested state.
|
||||||
|
</p>
|
||||||
|
<p><code>line</code> can also be an array of lines.</p>
|
||||||
|
<p>
|
||||||
|
By default, calling this method will trigger an automatic render (unless called
|
||||||
|
as a no-op). This can be disabled by passing <code>{render: false}</code> in the
|
||||||
|
options argument.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h3 id="API_isCollapsed">.isCollapsed</h3>
|
||||||
|
|
||||||
|
<pre data-lang="javascript">
|
||||||
|
collapsed = diagram.isCollapsed(line);
|
||||||
|
</pre>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
Returns true if the given line is marked as collapsed, regardless of whether
|
||||||
|
that line being collapsed has a meaningful impact on the rendered document.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h3 id="API_collapse">.collapse</h3>
|
||||||
|
|
||||||
|
<pre data-lang="javascript">
|
||||||
|
diagram.collapse(line, options);
|
||||||
|
diagram.collapse(line);
|
||||||
|
</pre>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
Shorthand for <code>.setCollapsed(line, true, options)</code>.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h3 id="API_expand">.expand</h3>
|
||||||
|
|
||||||
|
<pre data-lang="javascript">
|
||||||
|
diagram.expand(line, options);
|
||||||
|
diagram.expand(line);
|
||||||
|
</pre>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
Shorthand for <code>.setCollapsed(line, false, options)</code>.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h3 id="API_toggleCollapsed">.toggleCollapsed</h3>
|
||||||
|
|
||||||
|
<pre data-lang="javascript">
|
||||||
|
diagram.toggleCollapsed(line, options);
|
||||||
|
diagram.toggleCollapsed(line);
|
||||||
|
</pre>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
Toggles the given line’s collapsed status by calling
|
||||||
|
<code>.setCollapsed</code>.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h3 id="API_expandAll">.expandAll</h3>
|
||||||
|
|
||||||
|
<pre data-lang="javascript">
|
||||||
|
diagram.expandAll(options);
|
||||||
|
diagram.expandAll();
|
||||||
|
</pre>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
Marks all lines as non-collapsed. Returns true if a change occurred, or false
|
||||||
|
if all lines were already non-collapsed.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
By default, calling this method will trigger an automatic render (unless called
|
||||||
|
as a no-op). This can be disabled by passing <code>{render: false}</code> in the
|
||||||
|
options argument.
|
||||||
|
</p>
|
||||||
|
|
||||||
<h3 id="API_addEventListener">.addEventListener</h3>
|
<h3 id="API_addEventListener">.addEventListener</h3>
|
||||||
|
|
||||||
<pre data-lang="javascript">
|
<pre data-lang="javascript">
|
||||||
|
@ -623,9 +763,13 @@ Registers an event listener. The available events are:</p>
|
||||||
diagram.</li>
|
diagram.</li>
|
||||||
<li><code>click</code>: called when the user clicks on a region of the
|
<li><code>click</code>: called when the user clicks on a region of the
|
||||||
diagram.</li>
|
diagram.</li>
|
||||||
|
<li><code>dblclick</code>: called when the user double-clicks on a region of the
|
||||||
|
diagram.</li>
|
||||||
|
<li><code>render</code>: called when the diagram finishes rendering. Receives
|
||||||
|
the sequence diagram object as an argument.</li>
|
||||||
</ul>
|
</ul>
|
||||||
<p><code>mouseover</code> and <code>click</code> are invoked with a single
|
<p>All mouse events are invoked with a single parameter: the element. This
|
||||||
parameter: the element. This object contains:</p>
|
object contains:</p>
|
||||||
<ul>
|
<ul>
|
||||||
<li><code>ln</code>: the line number of the source code which defined the
|
<li><code>ln</code>: the line number of the source code which defined the
|
||||||
element.</li>
|
element.</li>
|
||||||
|
|
|
@ -216,6 +216,11 @@ define(['require'], (require) => {
|
||||||
registerListeners() {
|
registerListeners() {
|
||||||
this.code.addEventListener('input', () => this.update(false));
|
this.code.addEventListener('input', () => this.update(false));
|
||||||
|
|
||||||
|
this.diagram.addEventListener('render', () => {
|
||||||
|
this.updateMinSize(this.diagram.getSize());
|
||||||
|
this.pngDirty = true;
|
||||||
|
});
|
||||||
|
|
||||||
this.diagram.addEventListener('mouseover', (element) => {
|
this.diagram.addEventListener('mouseover', (element) => {
|
||||||
if(this.marker) {
|
if(this.marker) {
|
||||||
this.marker.clear();
|
this.marker.clear();
|
||||||
|
@ -256,6 +261,10 @@ define(['require'], (require) => {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.diagram.addEventListener('dblclick', (element) => {
|
||||||
|
this.diagram.toggleCollapsed(element.ln);
|
||||||
|
});
|
||||||
|
|
||||||
this.container.addEventListener('dragover', (event) => {
|
this.container.addEventListener('dragover', (event) => {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
if(hasDroppedFile(event, 'image/svg+xml')) {
|
if(hasDroppedFile(event, 'image/svg+xml')) {
|
||||||
|
@ -423,10 +432,8 @@ define(['require'], (require) => {
|
||||||
redraw(sequence) {
|
redraw(sequence) {
|
||||||
clearTimeout(this.debounced);
|
clearTimeout(this.debounced);
|
||||||
this.debounced = null;
|
this.debounced = null;
|
||||||
this.pngDirty = true;
|
|
||||||
this.renderedSeq = sequence;
|
this.renderedSeq = sequence;
|
||||||
this.diagram.render(sequence);
|
this.diagram.render(sequence);
|
||||||
this.updateMinSize(this.diagram.getSize());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
saveCode(src) {
|
saveCode(src) {
|
||||||
|
@ -481,6 +488,7 @@ define(['require'], (require) => {
|
||||||
} else {
|
} else {
|
||||||
this.code.value = code;
|
this.code.value = code;
|
||||||
}
|
}
|
||||||
|
this.diagram.expandAll({render: false});
|
||||||
this.update(true);
|
this.update(true);
|
||||||
this.diagram.setHighlight(null);
|
this.diagram.setHighlight(null);
|
||||||
}
|
}
|
||||||
|
|
|
@ -467,6 +467,7 @@ define(['core/ArrayUtilities'], (array) => {
|
||||||
blockType,
|
blockType,
|
||||||
tag: this.textFormatter(tag),
|
tag: this.textFormatter(tag),
|
||||||
label: this.textFormatter(label),
|
label: this.textFormatter(label),
|
||||||
|
canHide: true,
|
||||||
left: leftGAgent.id,
|
left: leftGAgent.id,
|
||||||
right: rightGAgent.id,
|
right: rightGAgent.id,
|
||||||
ln,
|
ln,
|
||||||
|
@ -612,6 +613,7 @@ define(['core/ArrayUtilities'], (array) => {
|
||||||
type: 'block begin',
|
type: 'block begin',
|
||||||
blockType,
|
blockType,
|
||||||
tag: this.textFormatter(tag),
|
tag: this.textFormatter(tag),
|
||||||
|
canHide: false,
|
||||||
label: this.textFormatter(label),
|
label: this.textFormatter(label),
|
||||||
left: details.leftGAgent.id,
|
left: details.leftGAgent.id,
|
||||||
right: details.rightGAgent.id,
|
right: details.rightGAgent.id,
|
||||||
|
|
|
@ -190,6 +190,7 @@ defineDescribe('Sequence Generator', ['./Generator'], (Generator) => {
|
||||||
blockBegin: (blockType, {
|
blockBegin: (blockType, {
|
||||||
tag = any(),
|
tag = any(),
|
||||||
label = any(),
|
label = any(),
|
||||||
|
canHide = any(),
|
||||||
left = any(),
|
left = any(),
|
||||||
right = any(),
|
right = any(),
|
||||||
ln = any(),
|
ln = any(),
|
||||||
|
@ -199,6 +200,7 @@ defineDescribe('Sequence Generator', ['./Generator'], (Generator) => {
|
||||||
blockType,
|
blockType,
|
||||||
tag,
|
tag,
|
||||||
label,
|
label,
|
||||||
|
canHide,
|
||||||
left,
|
left,
|
||||||
right,
|
right,
|
||||||
ln,
|
ln,
|
||||||
|
@ -1298,7 +1300,7 @@ defineDescribe('Sequence Generator', ['./Generator'], (Generator) => {
|
||||||
expect(sequence.stages).toEqual([
|
expect(sequence.stages).toEqual([
|
||||||
GENERATED.blockBegin(
|
GENERATED.blockBegin(
|
||||||
'if',
|
'if',
|
||||||
{tag: 'if!', label: 'abc!', ln: 10}
|
{tag: 'if!', label: 'abc!', canHide: true, ln: 10}
|
||||||
),
|
),
|
||||||
any(),
|
any(),
|
||||||
any(),
|
any(),
|
||||||
|
@ -1492,6 +1494,7 @@ defineDescribe('Sequence Generator', ['./Generator'], (Generator) => {
|
||||||
GENERATED.blockBegin('ref', {
|
GENERATED.blockBegin('ref', {
|
||||||
tag: 'ref!',
|
tag: 'ref!',
|
||||||
label: 'Foo!',
|
label: 'Foo!',
|
||||||
|
canHide: false,
|
||||||
left: bounds.left,
|
left: bounds.left,
|
||||||
right: bounds.right,
|
right: bounds.right,
|
||||||
}),
|
}),
|
||||||
|
|
|
@ -89,6 +89,7 @@ define([
|
||||||
this.knownThemeDefs = new Set();
|
this.knownThemeDefs = new Set();
|
||||||
this.knownDefs = new Set();
|
this.knownDefs = new Set();
|
||||||
this.highlights = new Map();
|
this.highlights = new Map();
|
||||||
|
this.collapsed = new Set();
|
||||||
this.currentHighlight = -1;
|
this.currentHighlight = -1;
|
||||||
this.buildStaticElements();
|
this.buildStaticElements();
|
||||||
this.components.forEach((component) => {
|
this.components.forEach((component) => {
|
||||||
|
@ -99,7 +100,6 @@ define([
|
||||||
_bindMethods() {
|
_bindMethods() {
|
||||||
this.separationStage = this.separationStage.bind(this);
|
this.separationStage = this.separationStage.bind(this);
|
||||||
this.renderStage = this.renderStage.bind(this);
|
this.renderStage = this.renderStage.bind(this);
|
||||||
this.addSeparation = this.addSeparation.bind(this);
|
|
||||||
this.addThemeDef = this.addThemeDef.bind(this);
|
this.addThemeDef = this.addThemeDef.bind(this);
|
||||||
this.addDef = this.addDef.bind(this);
|
this.addDef = this.addDef.bind(this);
|
||||||
}
|
}
|
||||||
|
@ -195,9 +195,37 @@ define([
|
||||||
info2.separations.set(agentID1, Math.max(d2, dist));
|
info2.separations.set(agentID1, Math.max(d2, dist));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
checkHidden(stage) {
|
||||||
|
const component = this.components.get(stage.type);
|
||||||
|
const env = {
|
||||||
|
renderer: this,
|
||||||
|
theme: this.theme,
|
||||||
|
agentInfos: this.agentInfos,
|
||||||
|
textSizer: this.sizer,
|
||||||
|
state: this.state,
|
||||||
|
components: this.components,
|
||||||
|
};
|
||||||
|
|
||||||
|
const hide = component.shouldHide(stage, env) || {};
|
||||||
|
|
||||||
|
const wasHidden = (this.hideNest > 0);
|
||||||
|
this.hideNest += hide.nest || 0;
|
||||||
|
const isHidden = (this.hideNest > 0);
|
||||||
|
|
||||||
|
if(this.hideNest < 0) {
|
||||||
|
throw new Error('Unexpected nesting in ' + stage.type);
|
||||||
|
}
|
||||||
|
if(wasHidden === isHidden) {
|
||||||
|
return isHidden;
|
||||||
|
} else {
|
||||||
|
return Boolean(hide.self);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
separationStage(stage) {
|
separationStage(stage) {
|
||||||
const agentSpaces = new Map();
|
const agentSpaces = new Map();
|
||||||
const agentIDs = this.visibleAgentIDs.slice();
|
const agentIDs = this.visibleAgentIDs.slice();
|
||||||
|
const seps = [];
|
||||||
|
|
||||||
const addSpacing = (agentID, {left, right}) => {
|
const addSpacing = (agentID, {left, right}) => {
|
||||||
const current = agentSpaces.get(agentID);
|
const current = agentSpaces.get(agentID);
|
||||||
|
@ -205,30 +233,47 @@ define([
|
||||||
current.right = Math.max(current.right, right);
|
current.right = Math.max(current.right, right);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const addSeparation = (agentID1, agentID2, dist) => {
|
||||||
|
seps.push({agentID1, agentID2, dist});
|
||||||
|
};
|
||||||
|
|
||||||
this.agentInfos.forEach((agentInfo) => {
|
this.agentInfos.forEach((agentInfo) => {
|
||||||
const rad = agentInfo.currentRad;
|
const rad = agentInfo.currentRad;
|
||||||
agentInfo.currentMaxRad = rad;
|
agentInfo.currentMaxRad = rad;
|
||||||
agentSpaces.set(agentInfo.id, {left: rad, right: rad});
|
agentSpaces.set(agentInfo.id, {left: rad, right: rad});
|
||||||
});
|
});
|
||||||
|
|
||||||
const env = {
|
const env = {
|
||||||
|
renderer: this,
|
||||||
theme: this.theme,
|
theme: this.theme,
|
||||||
agentInfos: this.agentInfos,
|
agentInfos: this.agentInfos,
|
||||||
visibleAgentIDs: this.visibleAgentIDs,
|
visibleAgentIDs: this.visibleAgentIDs,
|
||||||
momentaryAgentIDs: agentIDs,
|
momentaryAgentIDs: agentIDs,
|
||||||
textSizer: this.sizer,
|
textSizer: this.sizer,
|
||||||
addSpacing,
|
addSpacing,
|
||||||
addSeparation: this.addSeparation,
|
addSeparation,
|
||||||
state: this.state,
|
state: this.state,
|
||||||
components: this.components,
|
components: this.components,
|
||||||
};
|
};
|
||||||
|
|
||||||
const component = this.components.get(stage.type);
|
const component = this.components.get(stage.type);
|
||||||
if(!component) {
|
if(!component) {
|
||||||
throw new Error('Unknown component: ' + stage.type);
|
throw new Error('Unknown component: ' + stage.type);
|
||||||
}
|
}
|
||||||
|
|
||||||
component.separationPre(stage, env);
|
component.separationPre(stage, env);
|
||||||
component.separation(stage, env);
|
component.separation(stage, env);
|
||||||
|
|
||||||
|
if(this.checkHidden(stage)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
array.mergeSets(agentIDs, this.visibleAgentIDs);
|
array.mergeSets(agentIDs, this.visibleAgentIDs);
|
||||||
|
|
||||||
|
seps.forEach(({agentID1, agentID2, dist}) => {
|
||||||
|
this.addSeparation(agentID1, agentID2, dist);
|
||||||
|
});
|
||||||
|
|
||||||
agentIDs.forEach((agentIDR) => {
|
agentIDs.forEach((agentIDR) => {
|
||||||
const infoR = this.agentInfos.get(agentIDR);
|
const infoR = this.agentInfos.get(agentIDR);
|
||||||
const sepR = agentSpaces.get(agentIDR);
|
const sepR = agentSpaces.get(agentIDR);
|
||||||
|
@ -305,6 +350,13 @@ define([
|
||||||
list.push(o);
|
list.push(o);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
forwardEvent(source, sourceEvent, forwardEvent, forwardArgs) {
|
||||||
|
source.addEventListener(
|
||||||
|
sourceEvent,
|
||||||
|
this.trigger.bind(this, forwardEvent, forwardArgs)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
renderStage(stage) {
|
renderStage(stage) {
|
||||||
this.agentInfos.forEach((agentInfo) => {
|
this.agentInfos.forEach((agentInfo) => {
|
||||||
const rad = agentInfo.currentRad;
|
const rad = agentInfo.currentRad;
|
||||||
|
@ -312,6 +364,7 @@ define([
|
||||||
});
|
});
|
||||||
|
|
||||||
const envPre = {
|
const envPre = {
|
||||||
|
renderer: this,
|
||||||
theme: this.theme,
|
theme: this.theme,
|
||||||
agentInfos: this.agentInfos,
|
agentInfos: this.agentInfos,
|
||||||
textSizer: this.sizer,
|
textSizer: this.sizer,
|
||||||
|
@ -325,10 +378,6 @@ define([
|
||||||
|
|
||||||
const topY = this.checkAgentRange(agentIDs, asynchronousY);
|
const topY = this.checkAgentRange(agentIDs, asynchronousY);
|
||||||
|
|
||||||
const eventOut = () => {
|
|
||||||
this.trigger('mouseout');
|
|
||||||
};
|
|
||||||
|
|
||||||
const makeRegion = ({
|
const makeRegion = ({
|
||||||
stageOverride = null,
|
stageOverride = null,
|
||||||
unmasked = false,
|
unmasked = false,
|
||||||
|
@ -337,18 +386,16 @@ define([
|
||||||
const targetStage = (stageOverride || stage);
|
const targetStage = (stageOverride || stage);
|
||||||
this.addHighlightObject(targetStage.ln, o);
|
this.addHighlightObject(targetStage.ln, o);
|
||||||
o.setAttribute('class', 'region');
|
o.setAttribute('class', 'region');
|
||||||
o.addEventListener('mouseenter', () => {
|
this.forwardEvent(o, 'mouseenter', 'mouseover', [targetStage]);
|
||||||
this.trigger('mouseover', [targetStage]);
|
this.forwardEvent(o, 'mouseleave', 'mouseout', [targetStage]);
|
||||||
});
|
this.forwardEvent(o, 'click', 'click', [targetStage]);
|
||||||
o.addEventListener('mouseleave', eventOut);
|
this.forwardEvent(o, 'dblclick', 'dblclick', [targetStage]);
|
||||||
o.addEventListener('click', () => {
|
|
||||||
this.trigger('click', [targetStage]);
|
|
||||||
});
|
|
||||||
(unmasked ? this.unmaskedShapes : this.shapes).appendChild(o);
|
(unmasked ? this.unmaskedShapes : this.shapes).appendChild(o);
|
||||||
return o;
|
return o;
|
||||||
};
|
};
|
||||||
|
|
||||||
const env = {
|
const env = {
|
||||||
|
renderer: this,
|
||||||
topY,
|
topY,
|
||||||
primaryY: topY + topShift,
|
primaryY: topY + topShift,
|
||||||
fillLayer: this.backgroundFills,
|
fillLayer: this.backgroundFills,
|
||||||
|
@ -370,9 +417,15 @@ define([
|
||||||
components: this.components,
|
components: this.components,
|
||||||
};
|
};
|
||||||
|
|
||||||
const bottomY = Math.max(topY, component.render(stage, env) || 0);
|
let bottomY = topY;
|
||||||
this.markAgentRange(agentIDs, bottomY);
|
if(this.checkHidden(stage)) {
|
||||||
|
env.primaryY = topY;
|
||||||
|
component.renderHidden(stage, env);
|
||||||
|
} else {
|
||||||
|
bottomY = Math.max(bottomY, component.render(stage, env) || 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.markAgentRange(agentIDs, bottomY);
|
||||||
this.currentY = bottomY;
|
this.currentY = bottomY;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -477,6 +530,7 @@ define([
|
||||||
component.resetState(this.state);
|
component.resetState(this.state);
|
||||||
});
|
});
|
||||||
this.currentY = 0;
|
this.currentY = 0;
|
||||||
|
this.hideNest = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
_reset(theme) {
|
_reset(theme) {
|
||||||
|
@ -523,6 +577,49 @@ define([
|
||||||
this.currentHighlight = line;
|
this.currentHighlight = line;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
isCollapsed(line) {
|
||||||
|
return this.collapsed.has(line);
|
||||||
|
}
|
||||||
|
|
||||||
|
setCollapseAll(collapsed) {
|
||||||
|
if(collapsed) {
|
||||||
|
throw new Error('Cannot collapse all');
|
||||||
|
} else {
|
||||||
|
if(this.collapsed.size === 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
this.collapsed.clear();
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
_setCollapsed(line, collapsed) {
|
||||||
|
if(typeof line !== 'number') {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if(collapsed === this.isCollapsed(line)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if(collapsed) {
|
||||||
|
this.collapsed.add(line);
|
||||||
|
} else {
|
||||||
|
this.collapsed.delete(line);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
setCollapsed(line, collapsed = true) {
|
||||||
|
if(line === null) {
|
||||||
|
return this.setCollapseAll(collapsed);
|
||||||
|
}
|
||||||
|
if(Array.isArray(line)) {
|
||||||
|
return line
|
||||||
|
.map((ln) => this._setCollapsed(ln, collapsed))
|
||||||
|
.some((changed) => changed);
|
||||||
|
}
|
||||||
|
return this._setCollapsed(line, collapsed);
|
||||||
|
}
|
||||||
|
|
||||||
render(sequence) {
|
render(sequence) {
|
||||||
const prevHighlight = this.currentHighlight;
|
const prevHighlight = this.currentHighlight;
|
||||||
const oldTheme = this.theme;
|
const oldTheme = this.theme;
|
||||||
|
|
|
@ -77,9 +77,14 @@ define([
|
||||||
this.renderer = new Renderer(Object.assign({themes}, options));
|
this.renderer = new Renderer(Object.assign({themes}, options));
|
||||||
this.exporter = new Exporter();
|
this.exporter = new Exporter();
|
||||||
this.renderer.addEventForwarding(this);
|
this.renderer.addEventForwarding(this);
|
||||||
|
this.latestProcessed = null;
|
||||||
|
this.isInteractive = false;
|
||||||
if(options.container) {
|
if(options.container) {
|
||||||
options.container.appendChild(this.dom());
|
options.container.appendChild(this.dom());
|
||||||
}
|
}
|
||||||
|
if(options.interactive) {
|
||||||
|
this.addInteractivity();
|
||||||
|
}
|
||||||
if(typeof this.code === 'string') {
|
if(typeof this.code === 'string') {
|
||||||
this.render();
|
this.render();
|
||||||
}
|
}
|
||||||
|
@ -92,6 +97,7 @@ define([
|
||||||
themes: this.renderer.getThemes(),
|
themes: this.renderer.getThemes(),
|
||||||
namespace: null,
|
namespace: null,
|
||||||
components: this.renderer.components,
|
components: this.renderer.components,
|
||||||
|
interactive: this.isInteractive,
|
||||||
SVGTextBlockClass: this.renderer.SVGTextBlockClass,
|
SVGTextBlockClass: this.renderer.SVGTextBlockClass,
|
||||||
}, options));
|
}, options));
|
||||||
}
|
}
|
||||||
|
@ -114,10 +120,40 @@ define([
|
||||||
this.renderer.addTheme(theme);
|
this.renderer.addTheme(theme);
|
||||||
}
|
}
|
||||||
|
|
||||||
setHighlight(line = null) {
|
setHighlight(line) {
|
||||||
this.renderer.setHighlight(line);
|
this.renderer.setHighlight(line);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
isCollapsed(line) {
|
||||||
|
return this.renderer.isCollapsed(line);
|
||||||
|
}
|
||||||
|
|
||||||
|
setCollapsed(line, collapsed = true, {render = true} = {}) {
|
||||||
|
if(!this.renderer.setCollapsed(line, collapsed)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if(render && this.latestProcessed) {
|
||||||
|
this.render(this.latestProcessed);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
collapse(line, options) {
|
||||||
|
return this.setCollapsed(line, true, options);
|
||||||
|
}
|
||||||
|
|
||||||
|
expand(line, options) {
|
||||||
|
return this.setCollapsed(line, false, options);
|
||||||
|
}
|
||||||
|
|
||||||
|
toggleCollapsed(line, options) {
|
||||||
|
return this.setCollapsed(line, !this.isCollapsed(line), options);
|
||||||
|
}
|
||||||
|
|
||||||
|
expandAll(options) {
|
||||||
|
return this.setCollapsed(null, false, options);
|
||||||
|
}
|
||||||
|
|
||||||
getThemeNames() {
|
getThemeNames() {
|
||||||
return this.renderer.getThemeNames();
|
return this.renderer.getThemeNames();
|
||||||
}
|
}
|
||||||
|
@ -184,6 +220,8 @@ define([
|
||||||
processed = this.process(this.code);
|
processed = this.process(this.code);
|
||||||
}
|
}
|
||||||
this.renderer.render(processed);
|
this.renderer.render(processed);
|
||||||
|
this.latestProcessed = processed;
|
||||||
|
this.trigger('render', [this]);
|
||||||
} finally {
|
} finally {
|
||||||
if(dom.parentNode !== originalParent) {
|
if(dom.parentNode !== originalParent) {
|
||||||
document.body.removeChild(dom);
|
document.body.removeChild(dom);
|
||||||
|
@ -204,6 +242,17 @@ define([
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
addInteractivity() {
|
||||||
|
if(this.isInteractive) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.isInteractive = true;
|
||||||
|
|
||||||
|
this.addEventListener('click', (element) => {
|
||||||
|
this.toggleCollapsed(element.ln);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
extractCodeFromSVG(svg) {
|
extractCodeFromSVG(svg) {
|
||||||
return extractCodeFromSVG(svg);
|
return extractCodeFromSVG(svg);
|
||||||
}
|
}
|
||||||
|
@ -213,6 +262,17 @@ define([
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function datasetBoolean(value) {
|
||||||
|
return value !== undefined && value !== 'false';
|
||||||
|
}
|
||||||
|
|
||||||
|
function parseTagOptions(element) {
|
||||||
|
return {
|
||||||
|
namespace: element.dataset.sdNamespace || null,
|
||||||
|
interactive: datasetBoolean(element.dataset.sdInteractive),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
function convert(element, code = null, options = {}) {
|
function convert(element, code = null, options = {}) {
|
||||||
if(element.tagName === 'svg') {
|
if(element.tagName === 'svg') {
|
||||||
return null;
|
return null;
|
||||||
|
@ -224,7 +284,13 @@ define([
|
||||||
options = code;
|
options = code;
|
||||||
code = options.code;
|
code = options.code;
|
||||||
}
|
}
|
||||||
const diagram = new SequenceDiagram(code, options);
|
|
||||||
|
const tagOptions = parseTagOptions(element);
|
||||||
|
|
||||||
|
const diagram = new SequenceDiagram(
|
||||||
|
code,
|
||||||
|
Object.assign(tagOptions, options)
|
||||||
|
);
|
||||||
const newElement = diagram.dom();
|
const newElement = diagram.dom();
|
||||||
element.parentNode.insertBefore(newElement, element);
|
element.parentNode.insertBefore(newElement, element);
|
||||||
element.parentNode.removeChild(element);
|
element.parentNode.removeChild(element);
|
||||||
|
|
|
@ -138,4 +138,19 @@ defineDescribe('SequenceDiagram', [
|
||||||
'<polygon points="46 31 51 26 46 21"'
|
'<polygon points="46 31 51 26 46 21"'
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('renders collapsed blocks', () => {
|
||||||
|
diagram.set('if\nA -> B\nend');
|
||||||
|
diagram.setCollapsed(0, true);
|
||||||
|
|
||||||
|
const content = getSimplifiedContent(diagram);
|
||||||
|
|
||||||
|
expect(content).toContain('<svg viewBox="-5 -5 60 37">');
|
||||||
|
|
||||||
|
expect(content).toContain('<line x1="20" y1="5" x2="20" y2="27"');
|
||||||
|
expect(content).toContain('<line x1="30" y1="5" x2="30" y2="27"');
|
||||||
|
expect(content).toContain('<rect x="10" y="0" width="30" height="7"');
|
||||||
|
expect(content).toContain('<g class="region collapsed"');
|
||||||
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
|
@ -15,10 +15,31 @@ defineDescribe('SequenceDiagram Visuals', [
|
||||||
|
|
||||||
const IMAGE_BASE_PATH = 'scripts/sequence/test-images/';
|
const IMAGE_BASE_PATH = 'scripts/sequence/test-images/';
|
||||||
|
|
||||||
|
const COLLAPSE_REGEX = new RegExp(/# collapse/g);
|
||||||
|
|
||||||
|
function findCollapseLines(code) {
|
||||||
|
const results = [];
|
||||||
|
let p = 0;
|
||||||
|
let ln = -1;
|
||||||
|
while(true) {
|
||||||
|
const match = COLLAPSE_REGEX.exec(code);
|
||||||
|
if(!match) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
while(p !== -1 && p <= match.index) {
|
||||||
|
p = code.indexOf('\n', p) + 1;
|
||||||
|
++ ln;
|
||||||
|
}
|
||||||
|
results.push(ln);
|
||||||
|
}
|
||||||
|
return results;
|
||||||
|
}
|
||||||
|
|
||||||
function loadAndRenderSVG(svg, size = {resolution: RESOLUTION}) {
|
function loadAndRenderSVG(svg, size = {resolution: RESOLUTION}) {
|
||||||
const code = SequenceDiagram.extractCodeFromSVG(svg);
|
const code = SequenceDiagram.extractCodeFromSVG(svg);
|
||||||
|
|
||||||
const diagram = new SequenceDiagram(code);
|
const diagram = new SequenceDiagram(code);
|
||||||
|
diagram.collapse(findCollapseLines(code));
|
||||||
|
|
||||||
return Promise.all([
|
return Promise.all([
|
||||||
diagram.getCanvas(size).then(ImageRegion.fromCanvas),
|
diagram.getCanvas(size).then(ImageRegion.fromCanvas),
|
||||||
|
|
|
@ -332,6 +332,12 @@ define([
|
||||||
});
|
});
|
||||||
return maxEnd + env.theme.actionMargin;
|
return maxEnd + env.theme.actionMargin;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
renderHidden({agentIDs}, env) {
|
||||||
|
agentIDs.forEach((id) => {
|
||||||
|
env.drawAgentLine(id, env.topY, !this.begin);
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
BaseComponent.register('agent begin', new AgentCap(true));
|
BaseComponent.register('agent begin', new AgentCap(true));
|
||||||
|
|
|
@ -31,6 +31,10 @@ define(['./BaseComponent'], (BaseComponent) => {
|
||||||
});
|
});
|
||||||
return env.primaryY + env.theme.actionMargin;
|
return env.primaryY + env.theme.actionMargin;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
renderHidden(stage, env) {
|
||||||
|
this.render(stage, env);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
BaseComponent.register('agent highlight', new AgentHighlight());
|
BaseComponent.register('agent highlight', new AgentHighlight());
|
||||||
|
|
|
@ -10,6 +10,7 @@ define(() => {
|
||||||
}
|
}
|
||||||
|
|
||||||
separationPre(/*stage, {
|
separationPre(/*stage, {
|
||||||
|
renderer,
|
||||||
theme,
|
theme,
|
||||||
agentInfos,
|
agentInfos,
|
||||||
visibleAgentIDs,
|
visibleAgentIDs,
|
||||||
|
@ -23,6 +24,7 @@ define(() => {
|
||||||
}
|
}
|
||||||
|
|
||||||
separation(/*stage, {
|
separation(/*stage, {
|
||||||
|
renderer,
|
||||||
theme,
|
theme,
|
||||||
agentInfos,
|
agentInfos,
|
||||||
visibleAgentIDs,
|
visibleAgentIDs,
|
||||||
|
@ -36,6 +38,7 @@ define(() => {
|
||||||
}
|
}
|
||||||
|
|
||||||
renderPre(/*stage, {
|
renderPre(/*stage, {
|
||||||
|
renderer,
|
||||||
theme,
|
theme,
|
||||||
agentInfos,
|
agentInfos,
|
||||||
textSizer,
|
textSizer,
|
||||||
|
@ -46,6 +49,7 @@ define(() => {
|
||||||
}
|
}
|
||||||
|
|
||||||
render(/*stage, {
|
render(/*stage, {
|
||||||
|
renderer,
|
||||||
topY,
|
topY,
|
||||||
primaryY,
|
primaryY,
|
||||||
fillLayer,
|
fillLayer,
|
||||||
|
@ -61,6 +65,22 @@ define(() => {
|
||||||
}*/) {
|
}*/) {
|
||||||
// return bottom Y coordinate
|
// return bottom Y coordinate
|
||||||
}
|
}
|
||||||
|
|
||||||
|
renderHidden(/*stage, {
|
||||||
|
(same args as render, with primaryY = topY)
|
||||||
|
}*/) {
|
||||||
|
}
|
||||||
|
|
||||||
|
shouldHide(/*stage, {
|
||||||
|
renderer,
|
||||||
|
theme,
|
||||||
|
agentInfos,
|
||||||
|
textSizer,
|
||||||
|
state,
|
||||||
|
components,
|
||||||
|
}*/) {
|
||||||
|
// return {self, nest}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
BaseComponent.cleanRenderPreResult = ({
|
BaseComponent.cleanRenderPreResult = ({
|
||||||
|
|
|
@ -87,6 +87,12 @@ define([
|
||||||
'x2': agentInfoR.x,
|
'x2': agentInfoR.x,
|
||||||
'y2': y,
|
'y2': y,
|
||||||
}));
|
}));
|
||||||
|
} else if(blockInfo.canHide) {
|
||||||
|
clickable.setAttribute(
|
||||||
|
'class',
|
||||||
|
clickable.getAttribute('class') +
|
||||||
|
(blockInfo.hide ? ' collapsed' : ' expanded')
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return y + labelHeight + config.section.padding.top;
|
return y + labelHeight + config.section.padding.top;
|
||||||
|
@ -103,8 +109,11 @@ define([
|
||||||
}
|
}
|
||||||
|
|
||||||
storeBlockInfo(stage, env) {
|
storeBlockInfo(stage, env) {
|
||||||
|
const canHide = stage.canHide;
|
||||||
const blockInfo = {
|
const blockInfo = {
|
||||||
type: stage.blockType,
|
type: stage.blockType,
|
||||||
|
canHide,
|
||||||
|
hide: canHide && env.renderer.isCollapsed(stage.ln),
|
||||||
hold: null,
|
hold: null,
|
||||||
startY: null,
|
startY: null,
|
||||||
};
|
};
|
||||||
|
@ -142,6 +151,14 @@ define([
|
||||||
|
|
||||||
return super.render(stage, env, true);
|
return super.render(stage, env, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
shouldHide({left}, env) {
|
||||||
|
const blockInfo = env.state.blocks.get(left);
|
||||||
|
return {
|
||||||
|
self: false,
|
||||||
|
nest: blockInfo.hide ? 1 : 0,
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class BlockEnd extends BaseComponent {
|
class BlockEnd extends BaseComponent {
|
||||||
|
@ -165,7 +182,12 @@ define([
|
||||||
const agentInfoL = env.agentInfos.get(left);
|
const agentInfoL = env.agentInfos.get(left);
|
||||||
const agentInfoR = env.agentInfos.get(right);
|
const agentInfoR = env.agentInfos.get(right);
|
||||||
|
|
||||||
let shapes = config.boxRenderer({
|
let renderFn = config.boxRenderer;
|
||||||
|
if(blockInfo.hide) {
|
||||||
|
renderFn = config.collapsedBoxRenderer || renderFn;
|
||||||
|
}
|
||||||
|
|
||||||
|
let shapes = renderFn({
|
||||||
x: agentInfoL.x,
|
x: agentInfoL.x,
|
||||||
y: blockInfo.startY,
|
y: blockInfo.startY,
|
||||||
width: agentInfoR.x - agentInfoL.x,
|
width: agentInfoR.x - agentInfoL.x,
|
||||||
|
@ -185,6 +207,14 @@ define([
|
||||||
|
|
||||||
return env.primaryY + config.margin.bottom + env.theme.actionMargin;
|
return env.primaryY + config.margin.bottom + env.theme.actionMargin;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
shouldHide({left}, env) {
|
||||||
|
const blockInfo = env.state.blocks.get(left);
|
||||||
|
return {
|
||||||
|
self: false,
|
||||||
|
nest: blockInfo.hide ? -1 : 0,
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
BaseComponent.register('block begin', new BlockBegin());
|
BaseComponent.register('block begin', new BlockBegin());
|
||||||
|
|
|
@ -481,6 +481,10 @@ define([
|
||||||
});
|
});
|
||||||
return env.primaryY + env.theme.actionMargin;
|
return env.primaryY + env.theme.actionMargin;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
renderHidden(stage, env) {
|
||||||
|
this.render(stage, env);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class ConnectDelayEnd extends Connect {
|
class ConnectDelayEnd extends Connect {
|
||||||
|
|
|
@ -13,6 +13,10 @@ define(['./BaseComponent'], (BaseComponent) => {
|
||||||
render({name}, {topY, state}) {
|
render({name}, {topY, state}) {
|
||||||
state.marks.set(name, topY);
|
state.marks.set(name, topY);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
renderHidden(stage, env) {
|
||||||
|
this.render(stage, env);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class Async extends BaseComponent {
|
class Async extends BaseComponent {
|
||||||
|
|
|
@ -71,6 +71,27 @@ define([
|
||||||
env.makeRegion = originalMakeRegion;
|
env.makeRegion = originalMakeRegion;
|
||||||
return bottomY;
|
return bottomY;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
renderHidden(stage, env) {
|
||||||
|
stage.stages.forEach((subStage) => {
|
||||||
|
const component = env.components.get(subStage.type);
|
||||||
|
component.renderHidden(subStage, env);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
shouldHide(stage, env) {
|
||||||
|
const result = {
|
||||||
|
self: false,
|
||||||
|
nest: 0,
|
||||||
|
};
|
||||||
|
stage.stages.forEach((subStage) => {
|
||||||
|
const component = env.components.get(subStage.type);
|
||||||
|
const hide = component.shouldHide(subStage, env) || {};
|
||||||
|
result.self = (result.self || Boolean(hide.self));
|
||||||
|
result.nest += (hide.nest || 0);
|
||||||
|
});
|
||||||
|
return result;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
BaseComponent.register('parallel', new Parallel());
|
BaseComponent.register('parallel', new Parallel());
|
||||||
|
|
File diff suppressed because one or more lines are too long
After Width: | Height: | Size: 7.1 KiB |
|
@ -5,6 +5,7 @@ define([], [
|
||||||
'DividerMasking.svg',
|
'DividerMasking.svg',
|
||||||
'Asynchronous.svg',
|
'Asynchronous.svg',
|
||||||
'Block.svg',
|
'Block.svg',
|
||||||
|
'CollapsedBlocks.svg',
|
||||||
'Reference.svg',
|
'Reference.svg',
|
||||||
'ReferenceLayering.svg',
|
'ReferenceLayering.svg',
|
||||||
'Markdown.svg',
|
'Markdown.svg',
|
||||||
|
|
|
@ -258,6 +258,13 @@ define([
|
||||||
'rx': 2,
|
'rx': 2,
|
||||||
'ry': 2,
|
'ry': 2,
|
||||||
}),
|
}),
|
||||||
|
collapsedBoxRenderer: BaseTheme.renderRef.bind(null, {
|
||||||
|
'fill': '#FFFFFF',
|
||||||
|
'stroke': '#000000',
|
||||||
|
'stroke-width': 1.5,
|
||||||
|
'rx': 2,
|
||||||
|
'ry': 2,
|
||||||
|
}),
|
||||||
section: SHARED_BLOCK_SECTION,
|
section: SHARED_BLOCK_SECTION,
|
||||||
sepRenderer: SVGShapes.renderLine.bind(null, {
|
sepRenderer: SVGShapes.renderLine.bind(null, {
|
||||||
'stroke': '#000000',
|
'stroke': '#000000',
|
||||||
|
|
|
@ -269,6 +269,13 @@ define([
|
||||||
'rx': 5,
|
'rx': 5,
|
||||||
'ry': 5,
|
'ry': 5,
|
||||||
}),
|
}),
|
||||||
|
collapsedBoxRenderer: BaseTheme.renderRef.bind(null, {
|
||||||
|
'fill': '#FFFFFF',
|
||||||
|
'stroke': '#000000',
|
||||||
|
'stroke-width': 4,
|
||||||
|
'rx': 5,
|
||||||
|
'ry': 5,
|
||||||
|
}),
|
||||||
section: SHARED_BLOCK_SECTION,
|
section: SHARED_BLOCK_SECTION,
|
||||||
sepRenderer: SVGShapes.renderLine.bind(null, {
|
sepRenderer: SVGShapes.renderLine.bind(null, {
|
||||||
'stroke': '#000000',
|
'stroke': '#000000',
|
||||||
|
|
|
@ -261,6 +261,11 @@ define([
|
||||||
'stroke': '#000000',
|
'stroke': '#000000',
|
||||||
'stroke-width': 2,
|
'stroke-width': 2,
|
||||||
}),
|
}),
|
||||||
|
collapsedBoxRenderer: BaseTheme.renderRef.bind(null, {
|
||||||
|
'fill': '#FFFFFF',
|
||||||
|
'stroke': '#000000',
|
||||||
|
'stroke-width': 2,
|
||||||
|
}),
|
||||||
section: SHARED_BLOCK_SECTION,
|
section: SHARED_BLOCK_SECTION,
|
||||||
sepRenderer: SVGShapes.renderLine.bind(null, {
|
sepRenderer: SVGShapes.renderLine.bind(null, {
|
||||||
'stroke': '#000000',
|
'stroke': '#000000',
|
||||||
|
|
|
@ -230,6 +230,7 @@ define([
|
||||||
bottom: 0,
|
bottom: 0,
|
||||||
},
|
},
|
||||||
boxRenderer: null,
|
boxRenderer: null,
|
||||||
|
collapsedBoxRenderer: null,
|
||||||
section: SHARED_BLOCK_SECTION,
|
section: SHARED_BLOCK_SECTION,
|
||||||
sepRenderer: null,
|
sepRenderer: null,
|
||||||
},
|
},
|
||||||
|
@ -439,6 +440,8 @@ define([
|
||||||
|
|
||||||
this.blocks.ref.boxRenderer = this.renderRefBlock.bind(this);
|
this.blocks.ref.boxRenderer = this.renderRefBlock.bind(this);
|
||||||
this.blocks[''].boxRenderer = this.renderBlock.bind(this);
|
this.blocks[''].boxRenderer = this.renderBlock.bind(this);
|
||||||
|
this.blocks[''].collapsedBoxRenderer =
|
||||||
|
this.renderCollapsedBlock.bind(this);
|
||||||
this.blocks.ref.section.tag.boxRenderer = this.renderTag;
|
this.blocks.ref.section.tag.boxRenderer = this.renderTag;
|
||||||
this.blocks[''].section.tag.boxRenderer = this.renderTag;
|
this.blocks[''].section.tag.boxRenderer = this.renderTag;
|
||||||
this.blocks[''].sepRenderer = this.renderSeparator.bind(this);
|
this.blocks[''].sepRenderer = this.renderSeparator.bind(this);
|
||||||
|
@ -836,6 +839,10 @@ define([
|
||||||
return this.renderBox(position, {fill: 'none', thick: true});
|
return this.renderBox(position, {fill: 'none', thick: true});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
renderCollapsedBlock(position) {
|
||||||
|
return this.renderRefBlock(position);
|
||||||
|
}
|
||||||
|
|
||||||
renderTag({x, y, width, height}) {
|
renderTag({x, y, width, height}) {
|
||||||
const x2 = x + width;
|
const x2 = x + width;
|
||||||
const y2 = y + height;
|
const y2 = y + height;
|
||||||
|
|
|
@ -94,6 +94,17 @@ a:hover, a:active {
|
||||||
stroke: rgba(255, 128, 0, 0.5);
|
stroke: rgba(255, 128, 0, 0.5);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.sequence-diagram[data-sd-interactive] .region.collapsed,
|
||||||
|
.sequence-diagram[data-sd-interactive] .region.expanded {
|
||||||
|
cursor: pointer;
|
||||||
|
user-select: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sequence-diagram[data-sd-interactive] .region.collapsed:hover .outline,
|
||||||
|
.sequence-diagram[data-sd-interactive] .region.expanded:hover .outline {
|
||||||
|
fill: rgba(255, 128, 0, 0.5);
|
||||||
|
}
|
||||||
|
|
||||||
pre {
|
pre {
|
||||||
background: #EEEEEE;
|
background: #EEEEEE;
|
||||||
margin: 5px 0;
|
margin: 5px 0;
|
||||||
|
|
Loading…
Reference in New Issue