Beginnings of theme switching capability
This commit is contained in:
parent
5b6cbd518e
commit
f6f557bcd7
|
@ -11,13 +11,15 @@
|
|||
'sequence/Generator',
|
||||
'sequence/Renderer',
|
||||
'sequence/themes/Basic',
|
||||
'sequence/themes/Chunky',
|
||||
], (
|
||||
Interface,
|
||||
Exporter,
|
||||
Parser,
|
||||
Generator,
|
||||
Renderer,
|
||||
Theme
|
||||
BasicTheme,
|
||||
ChunkyTheme
|
||||
) => {
|
||||
const defaultCode = (
|
||||
'title Labyrinth\n' +
|
||||
|
@ -41,7 +43,10 @@
|
|||
defaultCode,
|
||||
parser: new Parser(),
|
||||
generator: new Generator(),
|
||||
renderer: new Renderer(new Theme()),
|
||||
renderer: new Renderer({themes: [
|
||||
new BasicTheme(),
|
||||
new ChunkyTheme(),
|
||||
]}),
|
||||
exporter: new Exporter(),
|
||||
localStorage: 'src',
|
||||
});
|
||||
|
|
|
@ -17,8 +17,18 @@
|
|||
return o;
|
||||
}
|
||||
|
||||
const FAVICON_SRC = (
|
||||
'theme chunky\n' +
|
||||
'define ABC as A, DEF as B\n' +
|
||||
'A -> B\n' +
|
||||
'B -> ]\n' +
|
||||
'] -> B\n' +
|
||||
'B -> A\n' +
|
||||
'terminators fade'
|
||||
);
|
||||
|
||||
const SAMPLE_REGEX = new RegExp(
|
||||
/<img src="screenshots\/([^"]*)"[^>]*>[\s]*```(?!shell).*\n([^]+?)```/g
|
||||
/<img src="([^"]*)"[^>]*>[\s]*```(?!shell).*\n([^]+?)```/g
|
||||
);
|
||||
|
||||
function findSamples(content) {
|
||||
|
@ -34,9 +44,23 @@
|
|||
code: match[2],
|
||||
});
|
||||
}
|
||||
results.push({
|
||||
file: 'favicon.png',
|
||||
code: FAVICON_SRC,
|
||||
height: 64,
|
||||
});
|
||||
return results;
|
||||
}
|
||||
|
||||
function filename(path) {
|
||||
const p = path.lastIndexOf('/');
|
||||
if(p !== -1) {
|
||||
return path.substr(p + 1);
|
||||
} else {
|
||||
return path;
|
||||
}
|
||||
}
|
||||
|
||||
const PNG_RESOLUTION = 4;
|
||||
|
||||
/* jshint -W072 */ // Allow several required modules
|
||||
|
@ -45,25 +69,30 @@
|
|||
'sequence/Generator',
|
||||
'sequence/Renderer',
|
||||
'sequence/themes/Basic',
|
||||
'sequence/themes/Chunky',
|
||||
'interface/Exporter',
|
||||
], (
|
||||
Parser,
|
||||
Generator,
|
||||
Renderer,
|
||||
Theme,
|
||||
BasicTheme,
|
||||
ChunkyTheme,
|
||||
Exporter
|
||||
) => {
|
||||
const parser = new Parser();
|
||||
const generator = new Generator();
|
||||
const theme = new Theme();
|
||||
const themes = [
|
||||
new BasicTheme(),
|
||||
new ChunkyTheme(),
|
||||
];
|
||||
|
||||
const status = makeNode('div', {'class': 'status'});
|
||||
const statusText = makeText('Loading\u2026');
|
||||
status.appendChild(statusText);
|
||||
document.body.appendChild(status);
|
||||
|
||||
function renderSample({file, code}) {
|
||||
const renderer = new Renderer(theme);
|
||||
function renderSample({file, code, height}) {
|
||||
const renderer = new Renderer({themes});
|
||||
const exporter = new Exporter();
|
||||
|
||||
const hold = makeNode('div', {'class': 'hold'});
|
||||
|
@ -78,12 +107,15 @@
|
|||
hold.appendChild(raster);
|
||||
|
||||
hold.appendChild(makeNode('img', {
|
||||
'src': 'screenshots/' + file,
|
||||
'src': file,
|
||||
'class': 'original',
|
||||
'title': 'original',
|
||||
}));
|
||||
|
||||
const downloadPNG = makeNode('a', {'href': '#', 'download': file});
|
||||
const downloadPNG = makeNode('a', {
|
||||
'href': '#',
|
||||
'download': filename(file),
|
||||
});
|
||||
downloadPNG.appendChild(makeText('Download PNG'));
|
||||
hold.appendChild(downloadPNG);
|
||||
|
||||
|
@ -92,7 +124,11 @@
|
|||
const parsed = parser.parse(code);
|
||||
const sequence = generator.generate(parsed);
|
||||
renderer.render(sequence);
|
||||
exporter.getPNGURL(renderer, PNG_RESOLUTION, (url) => {
|
||||
let resolution = PNG_RESOLUTION;
|
||||
if(height) {
|
||||
resolution = height / renderer.height;
|
||||
}
|
||||
exporter.getPNGURL(renderer, resolution, (url) => {
|
||||
raster.setAttribute('src', url);
|
||||
downloadPNG.setAttribute('href', url);
|
||||
});
|
||||
|
|
|
@ -121,6 +121,9 @@ define(['core/ArrayUtilities'], (array) => {
|
|||
'title': {type: 'keyword', suggest: true, then: {
|
||||
'': textToEnd,
|
||||
}},
|
||||
'theme': {type: 'keyword', suggest: true, then: {
|
||||
'': textToEnd,
|
||||
}},
|
||||
'terminators': {type: 'keyword', suggest: true, then: {
|
||||
'none': {type: 'keyword', suggest: true, then: {}},
|
||||
'cross': {type: 'keyword', suggest: true, then: {}},
|
||||
|
|
|
@ -539,6 +539,7 @@ define(['core/ArrayUtilities'], (array) => {
|
|||
return {
|
||||
meta: {
|
||||
title: meta.title,
|
||||
theme: meta.theme,
|
||||
},
|
||||
agents: this.agents.slice(),
|
||||
stages: globals.stages,
|
||||
|
|
|
@ -148,13 +148,13 @@ defineDescribe('Sequence Generator', ['./Generator'], (Generator) => {
|
|||
};
|
||||
|
||||
describe('.generate', () => {
|
||||
it('propagates title metadata', () => {
|
||||
it('propagates title and theme metadata', () => {
|
||||
const input = {
|
||||
meta: {title: 'bar'},
|
||||
meta: {title: 'bar', theme: 'zig', nope: 'skip'},
|
||||
stages: [],
|
||||
};
|
||||
const sequence = generator.generate(input);
|
||||
expect(sequence.meta).toEqual({title: 'bar'});
|
||||
expect(sequence.meta).toEqual({title: 'bar', theme: 'zig'});
|
||||
});
|
||||
|
||||
it('returns an empty sequence for blank input', () => {
|
||||
|
|
|
@ -188,6 +188,15 @@ define([
|
|||
return true;
|
||||
},
|
||||
|
||||
(line, meta) => { // theme
|
||||
if(tokenKeyword(line[0]) !== 'theme') {
|
||||
return null;
|
||||
}
|
||||
|
||||
meta.theme = joinLabel(line, 1);
|
||||
return true;
|
||||
},
|
||||
|
||||
(line, meta) => { // terminators
|
||||
if(tokenKeyword(line[0]) !== 'terminators') {
|
||||
return null;
|
||||
|
@ -356,6 +365,7 @@ define([
|
|||
const result = {
|
||||
meta: {
|
||||
title: '',
|
||||
theme: '',
|
||||
terminators: 'none',
|
||||
},
|
||||
stages: [],
|
||||
|
|
|
@ -33,6 +33,7 @@ defineDescribe('Sequence Parser', ['./Parser'], (Parser) => {
|
|||
expect(parsed).toEqual({
|
||||
meta: {
|
||||
title: '',
|
||||
theme: '',
|
||||
terminators: 'none',
|
||||
},
|
||||
stages: [],
|
||||
|
@ -44,6 +45,11 @@ defineDescribe('Sequence Parser', ['./Parser'], (Parser) => {
|
|||
expect(parsed.meta.title).toEqual('foo');
|
||||
});
|
||||
|
||||
it('reads theme metadata', () => {
|
||||
const parsed = parser.parse('theme foo');
|
||||
expect(parsed.meta.theme).toEqual('foo');
|
||||
});
|
||||
|
||||
it('reads terminators metadata', () => {
|
||||
const parsed = parser.parse('terminators bar');
|
||||
expect(parsed.meta.terminators).toEqual('bar');
|
||||
|
|
|
@ -63,8 +63,24 @@ define([
|
|||
};
|
||||
}
|
||||
|
||||
function makeThemes(themes) {
|
||||
if(themes.length === 0) {
|
||||
throw new Error('Cannot render without a theme');
|
||||
}
|
||||
const themeMap = new Map();
|
||||
themes.forEach((theme) => {
|
||||
themeMap.set(theme.name, theme);
|
||||
});
|
||||
themeMap.set('', themes[0]);
|
||||
return themeMap;
|
||||
}
|
||||
|
||||
let globalNamespace = 0;
|
||||
|
||||
return class Renderer {
|
||||
constructor(theme, {
|
||||
constructor({
|
||||
themes = [],
|
||||
namespace = null,
|
||||
components = null,
|
||||
SVGTextBlockClass = SVGShapes.TextBlock,
|
||||
} = {}) {
|
||||
|
@ -93,11 +109,16 @@ define([
|
|||
this.state = {};
|
||||
this.width = 0;
|
||||
this.height = 0;
|
||||
this.theme = theme;
|
||||
this.themes = makeThemes(themes);
|
||||
this.theme = null;
|
||||
this.namespace = namespace;
|
||||
if(namespace === null) {
|
||||
this.namespace = 'R' + globalNamespace;
|
||||
++ globalNamespace;
|
||||
}
|
||||
this.components = components;
|
||||
this.SVGTextBlockClass = SVGTextBlockClass;
|
||||
this.knownDefs = new Set();
|
||||
this.currentSequence = null;
|
||||
this.buildStaticElements();
|
||||
this.components.forEach((component) => {
|
||||
component.makeState(this.state);
|
||||
|
@ -112,11 +133,13 @@ define([
|
|||
|
||||
this.defs = svg.make('defs');
|
||||
this.mask = svg.make('mask', {
|
||||
'id': 'lineMask',
|
||||
'id': this.namespace + 'LineMask',
|
||||
'maskUnits': 'userSpaceOnUse',
|
||||
});
|
||||
this.maskReveal = svg.make('rect', {'fill': '#FFFFFF'});
|
||||
this.agentLines = svg.make('g', {'mask': 'url(#lineMask)'});
|
||||
this.agentLines = svg.make('g', {
|
||||
'mask': 'url(#' + this.namespace + 'LineMask)',
|
||||
});
|
||||
this.blocks = svg.make('g');
|
||||
this.sections = svg.make('g');
|
||||
this.actionShapes = svg.make('g');
|
||||
|
@ -133,11 +156,15 @@ define([
|
|||
}
|
||||
|
||||
addDef(name, generator) {
|
||||
const namespacedName = this.namespace + name;
|
||||
if(this.knownDefs.has(name)) {
|
||||
return;
|
||||
return namespacedName;
|
||||
}
|
||||
this.knownDefs.add(name);
|
||||
this.defs.appendChild(generator());
|
||||
const def = generator();
|
||||
def.setAttribute('id', namespacedName);
|
||||
this.defs.appendChild(def);
|
||||
return namespacedName;
|
||||
}
|
||||
|
||||
addSeparation(agentName1, agentName2, dist) {
|
||||
|
@ -517,16 +544,6 @@ define([
|
|||
this.height = (y1 - y0);
|
||||
}
|
||||
|
||||
setTheme(theme) {
|
||||
if(this.theme === theme) {
|
||||
return;
|
||||
}
|
||||
this.theme = theme;
|
||||
if(this.currentSequence) {
|
||||
this.render(this.currentSequence);
|
||||
}
|
||||
}
|
||||
|
||||
_reset() {
|
||||
this.knownDefs.clear();
|
||||
svg.empty(this.defs);
|
||||
|
@ -546,6 +563,12 @@ define([
|
|||
render(sequence) {
|
||||
this._reset();
|
||||
|
||||
const themeName = sequence.meta.theme;
|
||||
this.theme = this.themes.get(themeName);
|
||||
if(!this.theme) {
|
||||
this.theme = this.themes.get('');
|
||||
}
|
||||
|
||||
this.title.set({
|
||||
attrs: this.theme.titleAttrs,
|
||||
text: sequence.meta.title,
|
||||
|
@ -564,7 +587,6 @@ define([
|
|||
|
||||
this.sizer.resetCache();
|
||||
this.sizer.detach();
|
||||
this.currentSequence = sequence;
|
||||
}
|
||||
|
||||
getAgentX(name) {
|
||||
|
|
|
@ -3,14 +3,14 @@ defineDescribe('Sequence Renderer', [
|
|||
'./themes/Basic',
|
||||
], (
|
||||
Renderer,
|
||||
Theme
|
||||
BasicTheme
|
||||
) => {
|
||||
'use strict';
|
||||
|
||||
let renderer = null;
|
||||
|
||||
beforeEach(() => {
|
||||
renderer = new Renderer(new Theme());
|
||||
renderer = new Renderer({themes: [new BasicTheme()]});
|
||||
document.body.appendChild(renderer.svg());
|
||||
});
|
||||
|
||||
|
|
|
@ -155,11 +155,8 @@ define([
|
|||
render(y, {x, label}, env, isBegin) {
|
||||
const config = env.theme.agentCap.fade;
|
||||
|
||||
const gradID = isBegin ? 'fadeIn' : 'fadeOut';
|
||||
|
||||
env.addDef(gradID, () => {
|
||||
const gradID = env.addDef(isBegin ? 'FadeIn' : 'FadeOut', () => {
|
||||
const grad = svg.make('linearGradient', {
|
||||
'id': gradID,
|
||||
'x1': '0%',
|
||||
'y1': isBegin ? '100%' : '0%',
|
||||
'x2': '0%',
|
||||
|
|
|
@ -9,7 +9,7 @@ defineDescribe('Sequence Integration', [
|
|||
Parser,
|
||||
Generator,
|
||||
Renderer,
|
||||
Theme,
|
||||
BasicTheme,
|
||||
SVGTextBlock
|
||||
) => {
|
||||
'use strict';
|
||||
|
@ -20,10 +20,14 @@ defineDescribe('Sequence Integration', [
|
|||
let theme = null;
|
||||
|
||||
beforeEach(() => {
|
||||
theme = new Theme();
|
||||
theme = new BasicTheme();
|
||||
parser = new Parser();
|
||||
generator = new Generator();
|
||||
renderer = new Renderer(theme, {SVGTextBlockClass: SVGTextBlock});
|
||||
renderer = new Renderer({
|
||||
themes: [new BasicTheme()],
|
||||
namespace: '',
|
||||
SVGTextBlockClass: SVGTextBlock,
|
||||
});
|
||||
document.body.appendChild(renderer.svg());
|
||||
});
|
||||
|
||||
|
@ -45,12 +49,12 @@ defineDescribe('Sequence Integration', [
|
|||
expect(getSimplifiedContent(renderer)).toEqual(
|
||||
'<svg width="100%" height="100%" viewBox="-5 -5 10 10">' +
|
||||
'<defs>' +
|
||||
'<mask id="lineMask" maskUnits="userSpaceOnUse">' +
|
||||
'<mask id="LineMask" maskUnits="userSpaceOnUse">' +
|
||||
'<rect fill="#FFFFFF" x="-5" y="-5" width="10" height="10">' +
|
||||
'</rect>' +
|
||||
'</mask>' +
|
||||
'</defs>' +
|
||||
'<g mask="url(#lineMask)"></g>' +
|
||||
'<g mask="url(#LineMask)"></g>' +
|
||||
'</svg>'
|
||||
);
|
||||
});
|
||||
|
@ -63,12 +67,12 @@ defineDescribe('Sequence Integration', [
|
|||
expect(getSimplifiedContent(renderer)).toEqual(
|
||||
'<svg width="100%" height="100%" viewBox="-11.5 -16 23 21">' +
|
||||
'<defs>' +
|
||||
'<mask id="lineMask" maskUnits="userSpaceOnUse">' +
|
||||
'<mask id="LineMask" maskUnits="userSpaceOnUse">' +
|
||||
'<rect fill="#FFFFFF" x="-11.5" y="-16" width="23" height="21">' +
|
||||
'</rect>' +
|
||||
'</mask>' +
|
||||
'</defs>' +
|
||||
'<g mask="url(#lineMask)"></g>' +
|
||||
'<g mask="url(#LineMask)"></g>' +
|
||||
'<text' +
|
||||
' x="0"' +
|
||||
' font-family="sans-serif"' +
|
||||
|
|
|
@ -235,8 +235,9 @@ define(['core/ArrayUtilities', 'svg/SVGShapes'], (array, SVGShapes) => {
|
|||
},
|
||||
};
|
||||
|
||||
return class Theme {
|
||||
return class BasicTheme {
|
||||
constructor() {
|
||||
this.name = 'basic';
|
||||
Object.assign(this, SETTINGS);
|
||||
}
|
||||
};
|
||||
|
|
|
@ -1,8 +1,13 @@
|
|||
defineDescribe('Basic Theme', ['./Basic'], (Theme) => {
|
||||
defineDescribe('Basic Theme', ['./Basic'], (BasicTheme) => {
|
||||
'use strict';
|
||||
|
||||
const theme = new BasicTheme();
|
||||
|
||||
it('has a name', () => {
|
||||
expect(theme.name).toEqual('basic');
|
||||
});
|
||||
|
||||
it('contains settings for the theme', () => {
|
||||
const theme = new Theme();
|
||||
expect(theme.outerMargin).toEqual(5);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -0,0 +1,247 @@
|
|||
define(['core/ArrayUtilities', 'svg/SVGShapes'], (array, SVGShapes) => {
|
||||
'use strict';
|
||||
|
||||
const LINE_HEIGHT = 1.3;
|
||||
|
||||
const SETTINGS = {
|
||||
titleMargin: 10,
|
||||
outerMargin: 5,
|
||||
agentMargin: 8,
|
||||
actionMargin: 5,
|
||||
agentLineHighlightRadius: 4,
|
||||
|
||||
agentCap: {
|
||||
box: {
|
||||
padding: {
|
||||
top: 1,
|
||||
left: 3,
|
||||
right: 3,
|
||||
bottom: 1,
|
||||
},
|
||||
arrowBottom: 2 + 14 * 1.3 / 2,
|
||||
boxAttrs: {
|
||||
'fill': '#FFFFFF',
|
||||
'stroke': '#000000',
|
||||
'stroke-width': 3,
|
||||
'rx': 4,
|
||||
'ry': 4,
|
||||
},
|
||||
labelAttrs: {
|
||||
'font-family': 'sans-serif',
|
||||
'font-weight': 'bold',
|
||||
'font-size': 14,
|
||||
'line-height': LINE_HEIGHT,
|
||||
'text-anchor': 'middle',
|
||||
},
|
||||
},
|
||||
cross: {
|
||||
size: 20,
|
||||
attrs: {
|
||||
'fill': 'none',
|
||||
'stroke': '#000000',
|
||||
'stroke-width': 3,
|
||||
},
|
||||
},
|
||||
bar: {
|
||||
attrs: {
|
||||
'fill': '#000000',
|
||||
'stroke': '#000000',
|
||||
'stroke-width': 3,
|
||||
'height': 4,
|
||||
},
|
||||
},
|
||||
fade: {
|
||||
width: 5,
|
||||
height: 10,
|
||||
},
|
||||
none: {
|
||||
height: 10,
|
||||
},
|
||||
},
|
||||
|
||||
connect: {
|
||||
loopbackRadius: 6,
|
||||
lineAttrs: {
|
||||
'solid': {
|
||||
'fill': 'none',
|
||||
'stroke': '#000000',
|
||||
'stroke-width': 3,
|
||||
},
|
||||
'dash': {
|
||||
'fill': 'none',
|
||||
'stroke': '#000000',
|
||||
'stroke-width': 3,
|
||||
'stroke-dasharray': '4, 2',
|
||||
},
|
||||
},
|
||||
arrow: {
|
||||
width: 10,
|
||||
height: 12,
|
||||
attrs: {
|
||||
'fill': '#000000',
|
||||
'stroke-width': 0,
|
||||
'stroke-linejoin': 'miter',
|
||||
},
|
||||
},
|
||||
label: {
|
||||
padding: 6,
|
||||
margin: {top: 2, bottom: 1},
|
||||
attrs: {
|
||||
'font-family': 'sans-serif',
|
||||
'font-size': 8,
|
||||
'line-height': LINE_HEIGHT,
|
||||
'text-anchor': 'middle',
|
||||
},
|
||||
loopbackAttrs: {
|
||||
'font-family': 'sans-serif',
|
||||
'font-size': 8,
|
||||
'line-height': LINE_HEIGHT,
|
||||
},
|
||||
},
|
||||
mask: {
|
||||
padding: {
|
||||
top: 0,
|
||||
left: 3,
|
||||
right: 3,
|
||||
bottom: 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
block: {
|
||||
margin: {
|
||||
top: 0,
|
||||
bottom: 0,
|
||||
},
|
||||
boxAttrs: {
|
||||
'fill': 'none',
|
||||
'stroke': '#000000',
|
||||
'stroke-width': 1.5,
|
||||
'rx': 2,
|
||||
'ry': 2,
|
||||
},
|
||||
section: {
|
||||
padding: {
|
||||
top: 3,
|
||||
bottom: 2,
|
||||
},
|
||||
mode: {
|
||||
padding: {
|
||||
top: 1,
|
||||
left: 3,
|
||||
right: 3,
|
||||
bottom: 0,
|
||||
},
|
||||
boxAttrs: {
|
||||
'fill': '#FFFFFF',
|
||||
'stroke': '#000000',
|
||||
'stroke-width': 3,
|
||||
'rx': 2,
|
||||
'ry': 2,
|
||||
},
|
||||
labelAttrs: {
|
||||
'font-family': 'sans-serif',
|
||||
'font-weight': 'bold',
|
||||
'font-size': 9,
|
||||
'line-height': LINE_HEIGHT,
|
||||
'text-anchor': 'left',
|
||||
},
|
||||
},
|
||||
label: {
|
||||
padding: {
|
||||
top: 1,
|
||||
left: 5,
|
||||
right: 3,
|
||||
bottom: 0,
|
||||
},
|
||||
labelAttrs: {
|
||||
'font-family': 'sans-serif',
|
||||
'font-size': 8,
|
||||
'line-height': LINE_HEIGHT,
|
||||
'text-anchor': 'left',
|
||||
},
|
||||
},
|
||||
},
|
||||
separator: {
|
||||
attrs: {
|
||||
'stroke': '#000000',
|
||||
'stroke-width': 1.5,
|
||||
'stroke-dasharray': '4, 2',
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
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},
|
||||
overlap: {left: 10, right: 10},
|
||||
boxRenderer: SVGShapes.renderNote.bind(null, {
|
||||
'fill': '#FFFFFF',
|
||||
'stroke': '#000000',
|
||||
'stroke-width': 1,
|
||||
}, {
|
||||
'fill': 'none',
|
||||
'stroke': '#000000',
|
||||
'stroke-width': 1,
|
||||
}),
|
||||
labelAttrs: {
|
||||
'font-family': 'sans-serif',
|
||||
'font-size': 8,
|
||||
'line-height': LINE_HEIGHT,
|
||||
},
|
||||
},
|
||||
'state': {
|
||||
margin: {top: 0, left: 5, right: 5, bottom: 0},
|
||||
padding: {top: 7, left: 7, right: 7, bottom: 7},
|
||||
overlap: {left: 10, right: 10},
|
||||
boxRenderer: SVGShapes.renderBox.bind(null, {
|
||||
'fill': '#FFFFFF',
|
||||
'stroke': '#000000',
|
||||
'stroke-width': 1,
|
||||
'rx': 10,
|
||||
'ry': 10,
|
||||
}),
|
||||
labelAttrs: {
|
||||
'font-family': 'sans-serif',
|
||||
'font-size': 8,
|
||||
'line-height': LINE_HEIGHT,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
titleAttrs: {
|
||||
'font-family': 'sans-serif',
|
||||
'font-size': 20,
|
||||
'line-height': LINE_HEIGHT,
|
||||
'text-anchor': 'middle',
|
||||
'class': 'title',
|
||||
},
|
||||
|
||||
agentLineAttrs: {
|
||||
'fill': 'none',
|
||||
'stroke': '#000000',
|
||||
'stroke-width': 3,
|
||||
},
|
||||
};
|
||||
|
||||
return class ChunkyTheme {
|
||||
constructor() {
|
||||
this.name = 'chunky';
|
||||
Object.assign(this, SETTINGS);
|
||||
}
|
||||
};
|
||||
});
|
|
@ -0,0 +1,13 @@
|
|||
defineDescribe('Chunky Theme', ['./Chunky'], (ChunkyTheme) => {
|
||||
'use strict';
|
||||
|
||||
const theme = new ChunkyTheme();
|
||||
|
||||
it('has a name', () => {
|
||||
expect(theme.name).toEqual('chunky');
|
||||
});
|
||||
|
||||
it('contains settings for the theme', () => {
|
||||
expect(theme.outerMargin).toEqual(5);
|
||||
});
|
||||
});
|
|
@ -9,6 +9,7 @@ define([
|
|||
'sequence/Generator_spec',
|
||||
'sequence/Renderer_spec',
|
||||
'sequence/themes/Basic_spec',
|
||||
'sequence/themes/Chunky_spec',
|
||||
'sequence/components/AgentCap_spec',
|
||||
'sequence/components/AgentHighlight_spec',
|
||||
'sequence/components/Connect_spec',
|
||||
|
|
Loading…
Reference in New Issue