diff --git a/README.md b/README.md index 5e6b41a..d9f8167 100644 --- a/README.md +++ b/README.md @@ -75,6 +75,8 @@ note over Foo, Bar: "Foo and Bar on multiple lines" note between Foo, Bar: Link +text right: 'Comments\nOver here!' + state over Foo: Foo is ponderous ``` diff --git a/screenshots/NotesAndState.png b/screenshots/NotesAndState.png index d6aa957..3743742 100644 Binary files a/screenshots/NotesAndState.png and b/screenshots/NotesAndState.png differ diff --git a/scripts/sequence/Generator.js b/scripts/sequence/Generator.js index d1bd32a..a3c2304 100644 --- a/scripts/sequence/Generator.js +++ b/scripts/sequence/Generator.js @@ -11,6 +11,12 @@ define(['core/ArrayUtilities'], (array) => { const LOCKED_AGENT = new AgentState(false, true); const DEFAULT_AGENT = new AgentState(false); + const NOTE_DEFAULT_AGENTS = { + 'note over': ['[', ']'], + 'note left': ['['], + 'note right': [']'], + }; + return class Generator { constructor() { this.agentStates = new Map(); @@ -24,6 +30,9 @@ define(['core/ArrayUtilities'], (array) => { this.stageHandlers = { 'mark': this.handleMark.bind(this), 'async': this.handleAsync.bind(this), + 'note over': this.handleNote.bind(this), + 'note left': this.handleNote.bind(this), + 'note right': this.handleNote.bind(this), 'agent define': this.handleAgentDefine.bind(this), 'agent begin': this.handleAgentBegin.bind(this), 'agent end': this.handleAgentEnd.bind(this), @@ -131,6 +140,16 @@ define(['core/ArrayUtilities'], (array) => { this.currentSection.stages.push(stage); } + handleNote(stage) { + if(stage.agents.length === 0) { + this.handleUnknownStage(Object.assign({}, stage, { + agents: NOTE_DEFAULT_AGENTS[stage.type] || [], + })); + } else { + this.handleUnknownStage(stage); + } + } + handleAgentDefine({agents}) { array.mergeSets(this.currentNest.agents, agents); array.mergeSets(this.agents, agents); diff --git a/scripts/sequence/Generator_spec.js b/scripts/sequence/Generator_spec.js index 77bcc6f..ce1106b 100644 --- a/scripts/sequence/Generator_spec.js +++ b/scripts/sequence/Generator_spec.js @@ -481,6 +481,21 @@ defineDescribe('Sequence Generator', ['./Generator'], (Generator) => { ]})).toThrow(); }); + it('defaults to showing notes around the entire diagram', () => { + const sequence = generator.generate({stages: [ + {type: 'note right', agents: [], foo: 'bar'}, + {type: 'note left', agents: [], foo: 'bar'}, + {type: 'note over', agents: [], foo: 'bar'}, + {type: 'note right', agents: ['[']}, + ]}); + expect(sequence.stages).toEqual([ + {type: 'note right', agents: [']'], foo: 'bar'}, + {type: 'note left', agents: ['['], foo: 'bar'}, + {type: 'note over', agents: ['[', ']'], foo: 'bar'}, + {type: 'note right', agents: ['[']}, + ]); + }); + it('rejects attempts to change implicit agents', () => { expect(() => generator.generate({stages: [ {type: AGENT_BEGIN, agents: ['['], mode: 'box'}, diff --git a/scripts/sequence/Parser.js b/scripts/sequence/Parser.js index 54a71aa..9d46689 100644 --- a/scripts/sequence/Parser.js +++ b/scripts/sequence/Parser.js @@ -49,12 +49,19 @@ define(['core/ArrayUtilities'], (array) => { ]; const NOTE_TYPES = { + 'text': { + mode: 'text', + types: { + 'left': {type: 'note left', skip: ['of'], min: 0, max: null}, + 'right': {type: 'note right', skip: ['of'], min: 0, max: null}, + }, + }, 'note': { mode: 'note', types: { - 'over': {type: 'note over', skip: [], min: 1, max: null}, - 'left': {type: 'note left', skip: ['of'], min: 1, max: null}, - 'right': {type: 'note right', skip: ['of'], min: 1, max: null}, + 'over': {type: 'note over', skip: [], min: 0, max: null}, + 'left': {type: 'note left', skip: ['of'], min: 0, max: null}, + 'right': {type: 'note right', skip: ['of'], min: 0, max: null}, 'between': {type: 'note between', skip: [], min: 2, max: null}, }, }, diff --git a/scripts/sequence/Parser_spec.js b/scripts/sequence/Parser_spec.js index 7a5467a..9e45cae 100644 --- a/scripts/sequence/Parser_spec.js +++ b/scripts/sequence/Parser_spec.js @@ -325,7 +325,7 @@ defineDescribe('Sequence Parser', ['./Parser'], (Parser) => { }); it('rejects note between for a single agent', () => { - expect(() => parser.parse('state between A: hi')).toThrow(); + expect(() => parser.parse('note between A: hi')).toThrow(); }); it('converts state', () => { @@ -342,6 +342,16 @@ defineDescribe('Sequence Parser', ['./Parser'], (Parser) => { expect(() => parser.parse('state over A, B: hi')).toThrow(); }); + it('converts text blocks', () => { + const parsed = parser.parse('text right of A: doing stuff'); + expect(parsed.stages).toEqual([{ + type: 'note right', + agents: ['A'], + mode: 'text', + label: 'doing stuff', + }]); + }); + it('converts agent commands', () => { const parsed = parser.parse( 'define A, B\n' + diff --git a/scripts/sequence/Renderer.js b/scripts/sequence/Renderer.js index 02b76c9..a44b16b 100644 --- a/scripts/sequence/Renderer.js +++ b/scripts/sequence/Renderer.js @@ -170,6 +170,8 @@ define([ agents.forEach((agentR) => { const infoR = this.agentInfos.get(agentR); const sepR = agentSpaces.get(agentR) || SEP_ZERO; + infoR.maxRPad = Math.max(infoR.maxRPad, sepR.right); + infoR.maxLPad = Math.max(infoR.maxLPad, sepR.left); agents.forEach((agentL) => { const infoL = this.agentInfos.get(agentL); if(infoL.index >= infoR.index) { @@ -853,8 +855,6 @@ define([ } }); agentInfo.x = currentX; - this.minX = Math.min(this.minX, currentX); - this.maxX = Math.max(this.maxX, currentX); orderedInfos.push(agentInfo); }); @@ -873,6 +873,11 @@ define([ }); agentInfo.x = currentX; }); + + this.agentInfos.forEach(({label, x, maxRPad, maxLPad}) => { + this.minX = Math.min(this.minX, x - maxLPad); + this.maxX = Math.max(this.maxX, x + maxRPad); + }); } buildAgentInfos(agents, stages) { @@ -885,6 +890,8 @@ define([ x: null, latestYStart: null, latestY: 0, + maxRPad: 0, + maxLPad: 0, separations: new Map(), }); }); diff --git a/scripts/sequence/themes/Basic.js b/scripts/sequence/themes/Basic.js index a40f2e1..917e634 100644 --- a/scripts/sequence/themes/Basic.js +++ b/scripts/sequence/themes/Basic.js @@ -178,6 +178,19 @@ define([ }, note: { + 'text': { + margin: {top: 0, left: 2, right: 2, bottom: 0}, + padding: {top: 2, left: 2, right: 2, bottom: 2}, + overlap: {left: 10, right: 10}, + boxRenderer: SVGShapes.renderBox.bind(null, { + 'fill': '#FFFFFF', + }), + labelAttrs: { + 'font-family': 'sans-serif', + 'font-size': 8, + 'line-height': LINE_HEIGHT, + }, + }, 'note': { margin: {top: 0, left: 5, right: 5, bottom: 0}, padding: {top: 5, left: 5, right: 10, bottom: 5},