Add parts library
This commit is contained in:
parent
25ffd6a904
commit
85b4f99ccd
|
@ -28,6 +28,18 @@ define([
|
|||
events.forEach((event) => element.addEventListener(event, fn));
|
||||
}
|
||||
|
||||
function simplifyPreview(code) {
|
||||
code = code.replace(/\{Agent([0-9]*)\}/g, (match, num) => {
|
||||
if(num === undefined) {
|
||||
return 'A';
|
||||
} else {
|
||||
return String.fromCharCode('A'.charCodeAt(0) + Number(num) - 1);
|
||||
}
|
||||
});
|
||||
code = code.replace(/[{}]/g, '');
|
||||
return code;
|
||||
}
|
||||
|
||||
return class Interface {
|
||||
constructor({
|
||||
parser,
|
||||
|
@ -36,6 +48,7 @@ define([
|
|||
exporter,
|
||||
defaultCode = '',
|
||||
localStorage = '',
|
||||
library = null,
|
||||
}) {
|
||||
this.parser = parser;
|
||||
this.generator = generator;
|
||||
|
@ -43,6 +56,7 @@ define([
|
|||
this.exporter = exporter;
|
||||
this.defaultCode = defaultCode;
|
||||
this.localStorage = localStorage;
|
||||
this.library = library;
|
||||
this.minScale = 1.5;
|
||||
|
||||
this.debounced = null;
|
||||
|
@ -133,8 +147,8 @@ define([
|
|||
code.on('endCompletion', () => {
|
||||
lastKey = 0;
|
||||
});
|
||||
code.on('change', (cm) => {
|
||||
if(cm.state.completionActive) {
|
||||
code.on('change', (cm, change) => {
|
||||
if(cm.state.completionActive || change.origin === 'library') {
|
||||
return;
|
||||
}
|
||||
if(lastKey === 13 || lastKey === 8) {
|
||||
|
@ -193,12 +207,43 @@ define([
|
|||
});
|
||||
}
|
||||
|
||||
build(container) {
|
||||
const codePane = makeNode('div', {'class': 'pane-code'});
|
||||
const viewPane = makeNode('div', {'class': 'pane-view'});
|
||||
this.errorPane = makeNode('div', {'class': 'pane-error'});
|
||||
buildLibrary(container) {
|
||||
this.library.forEach((lib) => {
|
||||
const hold = makeNode('div', {
|
||||
'class': 'library-item',
|
||||
});
|
||||
const holdInner = makeNode('div', {
|
||||
'title': lib.title || lib.code,
|
||||
});
|
||||
hold.appendChild(holdInner);
|
||||
hold.addEventListener(
|
||||
'click',
|
||||
this.addCodeBlock.bind(this, lib.code)
|
||||
);
|
||||
container.appendChild(hold);
|
||||
try {
|
||||
const preview = simplifyPreview(lib.preview || lib.code);
|
||||
const parsed = this.parser.parse(preview);
|
||||
const generated = this.generator.generate(parsed);
|
||||
const rendering = this.renderer.clone();
|
||||
holdInner.appendChild(rendering.svg());
|
||||
rendering.render(generated);
|
||||
} catch(e) {
|
||||
hold.setAttribute('class', 'library-item broken');
|
||||
holdInner.appendChild(makeText(lib.code));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
buildErrorReport() {
|
||||
this.errorMsg = makeNode('div', {'class': 'msg-error'});
|
||||
this.errorText = makeText();
|
||||
this.errorPane.appendChild(this.errorText);
|
||||
this.errorMsg.appendChild(this.errorText);
|
||||
return this.errorMsg;
|
||||
}
|
||||
|
||||
buildViewPane() {
|
||||
const viewPane = makeNode('div', {'class': 'pane-view'});
|
||||
const viewPaneScroller = makeNode('div', {
|
||||
'class': 'pane-view-scroller',
|
||||
});
|
||||
|
@ -210,10 +255,31 @@ define([
|
|||
viewPaneScroller.appendChild(this.viewPaneInner);
|
||||
viewPane.appendChild(this.buildOptionsLinks());
|
||||
viewPane.appendChild(this.buildOptionsDownloads());
|
||||
viewPane.appendChild(this.buildErrorReport());
|
||||
|
||||
return viewPane;
|
||||
}
|
||||
|
||||
build(container) {
|
||||
const codePane = makeNode('div', {'class': 'pane-code'});
|
||||
container.appendChild(codePane);
|
||||
container.appendChild(this.errorPane);
|
||||
container.appendChild(viewPane);
|
||||
|
||||
if(this.library !== null) {
|
||||
const libPane = makeNode('div', {'class': 'pane-library'});
|
||||
const libPaneScroller = makeNode('div', {
|
||||
'class': 'pane-library-scroller',
|
||||
});
|
||||
const libPaneInner = makeNode('div', {
|
||||
'class': 'pane-library-inner',
|
||||
});
|
||||
libPaneScroller.appendChild(libPaneInner);
|
||||
libPane.appendChild(libPaneScroller);
|
||||
container.appendChild(libPane);
|
||||
codePane.setAttribute('class', 'pane-code reduced');
|
||||
this.buildLibrary(libPaneInner);
|
||||
}
|
||||
|
||||
container.appendChild(this.buildViewPane());
|
||||
|
||||
this.code = this.buildEditor(codePane);
|
||||
this.viewPaneInner.appendChild(this.renderer.svg());
|
||||
|
@ -222,6 +288,20 @@ define([
|
|||
this.update();
|
||||
}
|
||||
|
||||
addCodeBlock(block) {
|
||||
const cur = this.code.getCursor('head');
|
||||
const pos = {line: cur.line + ((cur.ch > 0) ? 1 : 0), ch: 0};
|
||||
const lines = block.split('\n').length;
|
||||
this.code.replaceRange(
|
||||
block + '\n',
|
||||
pos,
|
||||
null,
|
||||
'library'
|
||||
);
|
||||
this.code.setCursor({line: pos.line + lines, ch: 0});
|
||||
this.code.focus();
|
||||
}
|
||||
|
||||
updateMinSize(width, height) {
|
||||
const style = this.viewPaneInner.style;
|
||||
style.minWidth = Math.ceil(width * this.minScale) + 'px';
|
||||
|
@ -265,12 +345,12 @@ define([
|
|||
} else {
|
||||
this.errorText.nodeValue = error;
|
||||
}
|
||||
this.errorPane.setAttribute('class', 'pane-error error');
|
||||
this.errorMsg.setAttribute('class', 'msg-error error');
|
||||
}
|
||||
|
||||
markOK() {
|
||||
this.errorText.nodeValue = 'All OK';
|
||||
this.errorPane.setAttribute('class', 'pane-error ok');
|
||||
this.errorText.nodeValue = '';
|
||||
this.errorMsg.setAttribute('class', 'msg-error');
|
||||
}
|
||||
|
||||
update(immediate = true) {
|
||||
|
|
151
scripts/main.js
151
scripts/main.js
|
@ -40,6 +40,156 @@
|
|||
'\n' +
|
||||
'terminators box\n'
|
||||
);
|
||||
const library = [
|
||||
{
|
||||
title: 'Simple arrow',
|
||||
code: '{Agent1} -> {Agent2}: {Message}',
|
||||
},
|
||||
{
|
||||
title: 'Arrow with dotted line',
|
||||
code: '{Agent1} --> {Agent2}: {Message}',
|
||||
},
|
||||
{
|
||||
title: 'Open arrow',
|
||||
code: '{Agent1} ->> {Agent2}: {Message}',
|
||||
},
|
||||
{
|
||||
title: 'Self-connection',
|
||||
code: '{Agent1} -> {Agent1}: {Message}',
|
||||
},
|
||||
{
|
||||
title: 'Request/response pair',
|
||||
code: (
|
||||
'{Agent1} -> +{Agent2}: {Request}\n' +
|
||||
'{Agent1} <-- -{Agent2}: {Response}'
|
||||
),
|
||||
},
|
||||
{
|
||||
title: 'Inline agent creation / destruction',
|
||||
code: (
|
||||
'{Agent1} -> *{Agent2}: {Request}\n' +
|
||||
'{Agent1} <-- !{Agent2}: {Response}'
|
||||
),
|
||||
},
|
||||
{
|
||||
title: 'Agent creation / destruction',
|
||||
code: (
|
||||
'{Agent1} -> {Agent2}: {Request}\n' +
|
||||
'{Agent1} <-- {Agent2}: {Response}\n' +
|
||||
'end {Agent2}'
|
||||
),
|
||||
preview: (
|
||||
'begin A\n' +
|
||||
'::\n' +
|
||||
'A -> B: Request\n' +
|
||||
'A <-- B: Response\n' +
|
||||
'end B'
|
||||
),
|
||||
},
|
||||
{
|
||||
title: 'Conditional blocks',
|
||||
code: (
|
||||
'if {Condition1}\n' +
|
||||
' {Agent1} -> {Agent2}\n' +
|
||||
'else if {Condition2}\n' +
|
||||
' {Agent1} -> {Agent2}\n' +
|
||||
'else\n' +
|
||||
' {Agent1} -> {Agent2}\n' +
|
||||
'end'
|
||||
),
|
||||
preview: (
|
||||
'begin A, B\n' +
|
||||
'if Condition1\n' +
|
||||
' A -> B\n' +
|
||||
'else if Condition2\n' +
|
||||
' A -> B\n' +
|
||||
'else\n' +
|
||||
' A -> B\n' +
|
||||
'end'
|
||||
),
|
||||
},
|
||||
{
|
||||
title: 'Repeated blocks',
|
||||
code: (
|
||||
'repeat {Condition}\n' +
|
||||
' {Agent1} -> {Agent2}\n' +
|
||||
'end'
|
||||
),
|
||||
preview: (
|
||||
'begin A, B\n' +
|
||||
'repeat Condition\n' +
|
||||
' A -> B\n' +
|
||||
'end'
|
||||
),
|
||||
},
|
||||
{
|
||||
title: 'Note over agent',
|
||||
code: 'note over {Agent1}: {Message}',
|
||||
},
|
||||
{
|
||||
title: 'Note over multiple agents',
|
||||
code: 'note over {Agent1}, {Agent2}: {Message}',
|
||||
},
|
||||
{
|
||||
title: 'Note left of agent',
|
||||
code: 'note left of {Agent1}: {Message}',
|
||||
},
|
||||
{
|
||||
title: 'Note right of agent',
|
||||
code: 'note right of {Agent1}: {Message}',
|
||||
},
|
||||
{
|
||||
title: 'Note between agents',
|
||||
code: 'note between {Agent1}, {Agent2}: {Message}',
|
||||
},
|
||||
{
|
||||
title: 'State over agent',
|
||||
code: 'state over {Agent1}: {State}',
|
||||
},
|
||||
{
|
||||
title: 'Arrows to/from the sides',
|
||||
code: '[ -> {Agent1}: {Message1}\n{Agent1} -> ]: {Message2}',
|
||||
},
|
||||
{
|
||||
title: 'Text beside the diagram',
|
||||
code: 'text right: {Message}',
|
||||
preview: (
|
||||
'A -> B\n' +
|
||||
'simultaneously:\n' +
|
||||
'text right: "Message\\non the\\nside"'
|
||||
),
|
||||
},
|
||||
{
|
||||
title: 'Title',
|
||||
code: 'title {Title}',
|
||||
preview: 'title Title\nA -> B',
|
||||
},
|
||||
{
|
||||
title: 'Chunky theme',
|
||||
code: 'theme chunky',
|
||||
preview: 'theme chunky\nA -> B',
|
||||
},
|
||||
{
|
||||
title: 'Cross terminators',
|
||||
code: 'terminators cross',
|
||||
preview: 'A -> B\nterminators cross',
|
||||
},
|
||||
{
|
||||
title: 'Fade terminators',
|
||||
code: 'terminators fade',
|
||||
preview: 'A -> B\nterminators fade',
|
||||
},
|
||||
{
|
||||
title: 'Bar terminators',
|
||||
code: 'terminators bar',
|
||||
preview: 'A -> B\nterminators bar',
|
||||
},
|
||||
{
|
||||
title: 'Box terminators',
|
||||
code: 'terminators box',
|
||||
preview: 'A -> B\nterminators box',
|
||||
},
|
||||
];
|
||||
const ui = new Interface({
|
||||
defaultCode,
|
||||
parser: new Parser(),
|
||||
|
@ -49,6 +199,7 @@
|
|||
new ChunkyTheme(),
|
||||
]}),
|
||||
exporter: new Exporter(),
|
||||
library,
|
||||
localStorage: 'src',
|
||||
});
|
||||
ui.build(document.body);
|
||||
|
|
|
@ -135,6 +135,15 @@ define([
|
|||
});
|
||||
}
|
||||
|
||||
clone({namespace = null} = {}) {
|
||||
return new Renderer({
|
||||
themes: this.getThemes(),
|
||||
namespace,
|
||||
components: this.components,
|
||||
SVGTextBlockClass: this.SVGTextBlockClass,
|
||||
});
|
||||
}
|
||||
|
||||
buildStaticElements() {
|
||||
this.base = svg.makeContainer();
|
||||
|
||||
|
@ -625,6 +634,10 @@ define([
|
|||
);
|
||||
}
|
||||
|
||||
getThemes() {
|
||||
return this.getThemeNames().map((name) => this.themes.get(name));
|
||||
}
|
||||
|
||||
getAgentX(name) {
|
||||
return this.agentInfos.get(name).x;
|
||||
}
|
||||
|
|
|
@ -7,12 +7,16 @@ html, body {
|
|||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
bottom: 100px;
|
||||
bottom: 0;
|
||||
width: 30%;
|
||||
box-sizing: border-box;
|
||||
border-right: 1px solid #808080;
|
||||
}
|
||||
|
||||
.pane-code.reduced {
|
||||
bottom: 200px;
|
||||
}
|
||||
|
||||
.pane-code .CodeMirror {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
|
@ -67,29 +71,88 @@ html, body {
|
|||
height: 100%;
|
||||
}
|
||||
|
||||
.pane-error {
|
||||
.pane-library {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
width: 30%;
|
||||
height: 200px;
|
||||
box-sizing: border-box;
|
||||
background: #EEEEEE;
|
||||
border-top: 1px solid #808080;
|
||||
border-right: 1px solid #808080;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.pane-library-scroller {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.pane-library-inner {
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
.library-item {
|
||||
display: inline-block;
|
||||
width: 80px;
|
||||
height: 80px;
|
||||
}
|
||||
|
||||
.library-item > div {
|
||||
width: 80px;
|
||||
height: 80px;
|
||||
border: 2px solid #EEEEEE;
|
||||
background: #FFFFFF;
|
||||
box-sizing: border-box;
|
||||
cursor: pointer;
|
||||
overflow: hidden;
|
||||
transition: transform 0.2s, border-color 0.2s, background 0.2s;
|
||||
}
|
||||
|
||||
.library-item.broken > div {
|
||||
padding: 5px;
|
||||
font: 6px monospace;
|
||||
white-space: pre;
|
||||
}
|
||||
|
||||
.library-item svg {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.library-item:hover > div {
|
||||
border-color: #FFCC00;
|
||||
background: #FFFFDD;
|
||||
/* position: absolute;*/
|
||||
transform: scale(1.1);
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
.msg-error {
|
||||
display: none;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 20%;
|
||||
margin-top: -50px;
|
||||
width: 60%;
|
||||
height: 100px;
|
||||
overflow: auto;
|
||||
box-sizing: border-box;
|
||||
padding: 5px 10px;
|
||||
font-family: monospace;
|
||||
background: #DDDDDD;
|
||||
border-top: 1px solid #808080;
|
||||
border-right: 1px solid #808080;
|
||||
}
|
||||
|
||||
.pane-error.ok {
|
||||
color: #007700;
|
||||
background: #E8EEE8;
|
||||
}
|
||||
|
||||
.pane-error.error {
|
||||
color: #770000;
|
||||
background: #EEE8E8;
|
||||
border: 1px solid rgba(0, 0, 0, 0.5);
|
||||
border-radius: 5px;
|
||||
box-shadow: 0 0 30px rgba(0, 0, 0, 0.2);
|
||||
opacity: 0.95;
|
||||
z-index: 4;
|
||||
}
|
||||
|
||||
.msg-error.error {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.options {
|
||||
|
|
Loading…
Reference in New Issue