[skip CI] WIP node sub text
This commit is contained in:
@@ -175,7 +175,7 @@ Will produce the following diagram:
|
|||||||
| `placing.max-link-length` | 3 | maximum stretching of links between nodes | no |
|
| `placing.max-link-length` | 3 | maximum stretching of links between nodes | no |
|
||||||
| `placing.diagonals` | `true` | allow diagonal links to be made | no |
|
| `placing.diagonals` | `true` | allow diagonal links to be made | no |
|
||||||
| `rendering.beautify` | `false` | output a readable SVG file | no |
|
| `rendering.beautify` | `false` | output a readable SVG file | no |
|
||||||
| `rendering.scale` | 128 | (in pixels) final icons size | no |
|
| `rendering.scale` | 256 | (in pixels) final icons size | no |
|
||||||
| `rendering.color` | `black` | color of all elements | no |
|
| `rendering.color` | `black` | color of all elements | no |
|
||||||
| `rendering.h-spacing` | 1.3 | how width is stretched comparing to height | no |
|
| `rendering.h-spacing` | 1.3 | how width is stretched comparing to height | no |
|
||||||
| `rendering.icons.scale` | 1 | default scaling of icons | in node or sub-icon |
|
| `rendering.icons.scale` | 1 | default scaling of icons | in node or sub-icon |
|
||||||
@@ -184,9 +184,11 @@ Will produce the following diagram:
|
|||||||
| `rendering.links.color` | `''` | color of all links (might be redefined in link definition) | in link |
|
| `rendering.links.color` | `''` | color of all links (might be redefined in link definition) | in link |
|
||||||
| `rendering.links.size` | 0 | forced size/length of the links (0 means it will be computed from the distance between the nodes) | in link |
|
| `rendering.links.size` | 0 | forced size/length of the links (0 means it will be computed from the distance between the nodes) | in link |
|
||||||
| `rendering.texts.font` | `'sans-serif'` | font family of the texts (might be redefined in sub-elements definition) | in text |
|
| `rendering.texts.font` | `'sans-serif'` | font family of the texts (might be redefined in sub-elements definition) | in text |
|
||||||
| `rendering.texts.font-size` | 20 | font size of the texts | in sub-text |
|
| `rendering.texts.font-size` | 12 | font size of the texts | in sub-text |
|
||||||
| `rendering.texts.font-style` | `'normal'` | font style of the texts (see [Font styles](#font-styles)) | in sub-text |
|
| `rendering.texts.font-style` | `'normal'` | font style of the texts (see [Font styles](#font-styles)) | in sub-text |
|
||||||
| `rendering.texts.color` | `''` | color of all texts | in sub-text |
|
| `rendering.texts.color` | `''` | color of all texts | in sub-text |
|
||||||
|
| `rendering.texts.margin` | 0.35 | margin between texts and elements | in sub-text |
|
||||||
|
| `rendering.texts.line-height` | 1.2 | height of each line in font size | in sub-text |
|
||||||
|
|
||||||
### `nodes`
|
### `nodes`
|
||||||
|
|
||||||
@@ -231,6 +233,8 @@ You can **just enter a string** to be considered a text but you can define a tex
|
|||||||
| `font` | string | no | redefine the font family |
|
| `font` | string | no | redefine the font family |
|
||||||
| `font-size` | number | no | redefine the font size |
|
| `font-size` | number | no | redefine the font size |
|
||||||
| `font-style` | string | no | redefine the font style (see [Font styles](#font-styles)) |
|
| `font-style` | string | no | redefine the font style (see [Font styles](#font-styles)) |
|
||||||
|
| `margin` | number | no | redefine the margin with the parent element |
|
||||||
|
| `line-height` | number | no | height of each line in font size |
|
||||||
|
|
||||||
### Icons
|
### Icons
|
||||||
|
|
||||||
|
|||||||
+8
-2
@@ -40,6 +40,7 @@ const faDiagrams = require('./src/index');
|
|||||||
const data = {
|
const data = {
|
||||||
options: {
|
options: {
|
||||||
rendering: {
|
rendering: {
|
||||||
|
beautify: true,
|
||||||
icons: {
|
icons: {
|
||||||
color: '#4E342E'
|
color: '#4E342E'
|
||||||
}
|
}
|
||||||
@@ -48,12 +49,16 @@ const data = {
|
|||||||
nodes: [
|
nodes: [
|
||||||
{
|
{
|
||||||
name: 'node1',
|
name: 'node1',
|
||||||
icon: 'server',
|
icon: 'laptop-code',
|
||||||
|
bottom: 'my app',
|
||||||
|
top:'my app',
|
||||||
|
left:'my\napp'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'node2',
|
name: 'node2',
|
||||||
icon: 'globe',
|
icon: 'globe',
|
||||||
color: '#455A64',
|
color: '#455A64',
|
||||||
|
bottom: 'world'
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
links: [
|
links: [
|
||||||
@@ -61,10 +66,11 @@ const data = {
|
|||||||
from: 'node1',
|
from: 'node1',
|
||||||
to: 'node2',
|
to: 'node2',
|
||||||
color: '#333333',
|
color: '#333333',
|
||||||
|
bottom: 'hello'
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
};
|
};
|
||||||
|
|
||||||
fs.writeFileSync('docs/sample.json', JSON.stringify(data), {encoding: 'utf-8'});
|
fs.writeFileSync('docs/sample.json', JSON.stringify(data, null, 4), {encoding: 'utf-8'});
|
||||||
|
|
||||||
fs.writeFileSync('preview/sample.svg', faDiagrams.compute(data), {encoding: 'utf-8'});
|
fs.writeFileSync('preview/sample.svg', faDiagrams.compute(data), {encoding: 'utf-8'});
|
||||||
+104
-7
@@ -26,6 +26,10 @@ try {
|
|||||||
* @property {number} x
|
* @property {number} x
|
||||||
* @property {number} y
|
* @property {number} y
|
||||||
* @property {string|{path:string,width:number:height:number}} icon
|
* @property {string|{path:string,width:number:height:number}} icon
|
||||||
|
* @property {Object|undefined} bottom
|
||||||
|
* @property {Object|undefined} top
|
||||||
|
* @property {Object|undefined} left
|
||||||
|
* @property {Object|undefined} right
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -33,6 +37,8 @@ try {
|
|||||||
* @property {string} from
|
* @property {string} from
|
||||||
* @property {string} to
|
* @property {string} to
|
||||||
* @property {string|undefined} type
|
* @property {string|undefined} type
|
||||||
|
* @property {Object|undefined} bottom
|
||||||
|
* @property {Object|undefined} top
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const SUB_DEF = {
|
const SUB_DEF = {
|
||||||
@@ -48,7 +54,9 @@ const SUB_DEF = {
|
|||||||
'font': 'string',
|
'font': 'string',
|
||||||
'font-size': 'number',
|
'font-size': 'number',
|
||||||
'font-style': 'string',
|
'font-style': 'string',
|
||||||
'scale': 'number',
|
'margin': 'number',
|
||||||
|
'line-height': 'number',
|
||||||
|
'scale': 'number'
|
||||||
};
|
};
|
||||||
|
|
||||||
const NODE_DEF = {
|
const NODE_DEF = {
|
||||||
@@ -82,7 +90,7 @@ const LINK_DEF = {
|
|||||||
|
|
||||||
const DEFAULT_OPTIONS = {
|
const DEFAULT_OPTIONS = {
|
||||||
'beautify': false,
|
'beautify': false,
|
||||||
'scale': 128,
|
'scale': 256,
|
||||||
'h-spacing': 1.3,
|
'h-spacing': 1.3,
|
||||||
'color': 'black',
|
'color': 'black',
|
||||||
'icons': {
|
'icons': {
|
||||||
@@ -96,9 +104,11 @@ const DEFAULT_OPTIONS = {
|
|||||||
},
|
},
|
||||||
'texts': {
|
'texts': {
|
||||||
'font': 'sans-serif',
|
'font': 'sans-serif',
|
||||||
'font-size': '20',
|
'font-size': 12,
|
||||||
'font-style': 'normal',
|
'font-style': 'normal',
|
||||||
'color': ''
|
'color': '',
|
||||||
|
'margin': 0.2,
|
||||||
|
'line-height': 1.2
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -225,6 +235,30 @@ module.exports = (options) => {
|
|||||||
return {w: maxX + 1, h: maxY + 1};
|
return {w: maxX + 1, h: maxY + 1};
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} text
|
||||||
|
* @param {number} lineHeight
|
||||||
|
* @param {string} anchor
|
||||||
|
* @return {Object}
|
||||||
|
*/
|
||||||
|
getSvgText: (text, lineHeight, anchor) => {
|
||||||
|
text = text.trim();
|
||||||
|
if (!text.includes('\n'))
|
||||||
|
return {'_text': text};
|
||||||
|
const list = [];
|
||||||
|
text.split('\n').map(t => t.trim()).forEach(line => {
|
||||||
|
list.push({
|
||||||
|
'_attributes': {
|
||||||
|
'x': 0,
|
||||||
|
'dy': `${lineHeight}em`,
|
||||||
|
'text-anchor': anchor
|
||||||
|
},
|
||||||
|
'_text': line
|
||||||
|
});
|
||||||
|
});
|
||||||
|
return {'tspan': list};
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {Node2} node
|
* @param {Node2} node
|
||||||
*/
|
*/
|
||||||
@@ -233,11 +267,11 @@ module.exports = (options) => {
|
|||||||
if (!icon)
|
if (!icon)
|
||||||
return null;
|
return null;
|
||||||
const scale = (node['scale'] || options['icons']['scale']) * DEFAULT_SCALE;
|
const scale = (node['scale'] || options['icons']['scale']) * DEFAULT_SCALE;
|
||||||
return {
|
const g = {
|
||||||
'_attributes': {
|
'_attributes': {
|
||||||
'transform': `translate(${(node.x + 0.5) * options['h-spacing']} ${node.y + 0.5})`,
|
'transform': `translate(${(node.x + 0.5) * options['h-spacing']} ${node.y + 0.5})`,
|
||||||
},
|
},
|
||||||
'g': {
|
'g': [{
|
||||||
'_attributes': {
|
'_attributes': {
|
||||||
'transform': `scale(${scale / icon.height} ${scale / icon.height}) translate(${-icon.width / 2} ${-icon.height / 2})`,
|
'transform': `scale(${scale / icon.height} ${scale / icon.height}) translate(${-icon.width / 2} ${-icon.height / 2})`,
|
||||||
'stroke': (node['color'] || options['icons']['color'] || undefined),
|
'stroke': (node['color'] || options['icons']['color'] || undefined),
|
||||||
@@ -248,8 +282,59 @@ module.exports = (options) => {
|
|||||||
'd': icon.path,
|
'd': icon.path,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}]
|
||||||
};
|
};
|
||||||
|
|
||||||
|
['bottom', 'top', 'left', 'right'].forEach(side => {
|
||||||
|
const subE = node[side];
|
||||||
|
if (subE && subE.text) {
|
||||||
|
const fontSize = subE['font-size'] || options['texts']['font-size'];
|
||||||
|
const margin = subE['margin'] || options['texts']['margin'];
|
||||||
|
let pos;
|
||||||
|
let anchor;
|
||||||
|
switch (side) {
|
||||||
|
case 'bottom':
|
||||||
|
pos = {x: 0, y: 1};
|
||||||
|
anchor = 'middle';
|
||||||
|
break;
|
||||||
|
case 'top':
|
||||||
|
pos = {x: 0, y: -1};
|
||||||
|
anchor = 'middle';
|
||||||
|
break;
|
||||||
|
case 'left':
|
||||||
|
pos = {x: -1, y: 0};
|
||||||
|
anchor = 'end';
|
||||||
|
break;
|
||||||
|
case 'right':
|
||||||
|
pos = {x: 1, y: 0};
|
||||||
|
anchor = 'start';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
const text = self.getSvgText(subE.text, subE['line-height'] || options['texts']['line-height'], anchor);
|
||||||
|
|
||||||
|
const textHeight = text['tspan'] ? text['tspan'].length : 0;
|
||||||
|
|
||||||
|
text['_attributes'] = {
|
||||||
|
'font-family': subE['font'] || options['texts']['font'],
|
||||||
|
'font-size': fontSize,
|
||||||
|
'text-anchor': anchor,
|
||||||
|
'x': pos.x * fontSize,
|
||||||
|
'y': (pos.y + 0.25) * fontSize - textHeight * fontSize
|
||||||
|
};
|
||||||
|
|
||||||
|
g['g'].push({
|
||||||
|
'_attributes': {
|
||||||
|
'transform': `translate(${pos.x * margin} ${pos.y * margin}) scale(${1 / (options['scale'] * DEFAULT_SCALE)} ${1 / (options['scale'] * DEFAULT_SCALE)})`,
|
||||||
|
'stroke': (subE['color'] || node['color'] || options['texts']['color'] || options['icons']['color'] || undefined),
|
||||||
|
'fill': (subE['color'] || node['color'] || options['texts']['color'] || options['icons']['color'] || undefined)
|
||||||
|
},
|
||||||
|
'text': text
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return g;
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -343,6 +428,12 @@ module.exports = (options) => {
|
|||||||
const res = utils.isValid(nodes[key], NODE_DEF);
|
const res = utils.isValid(nodes[key], NODE_DEF);
|
||||||
if (res)
|
if (res)
|
||||||
throw `Node '${key}' is invalid at key '${res}'`;
|
throw `Node '${key}' is invalid at key '${res}'`;
|
||||||
|
|
||||||
|
['bottom', 'top', 'left', 'right'].forEach(sub => {
|
||||||
|
if (typeof nodes[key][sub] === 'string')
|
||||||
|
nodes[key][sub] = {text: nodes[key][sub]};
|
||||||
|
});
|
||||||
|
|
||||||
const group = self.renderNode(nodes[key]);
|
const group = self.renderNode(nodes[key]);
|
||||||
if (group)
|
if (group)
|
||||||
data['g'].push(group);
|
data['g'].push(group);
|
||||||
@@ -352,6 +443,12 @@ module.exports = (options) => {
|
|||||||
const res = utils.isValid(link, LINK_DEF);
|
const res = utils.isValid(link, LINK_DEF);
|
||||||
if (res)
|
if (res)
|
||||||
throw `Link ${i} (${link.from}->${link.to}) is invalid at key '${res}'`;
|
throw `Link ${i} (${link.from}->${link.to}) is invalid at key '${res}'`;
|
||||||
|
|
||||||
|
['bottom', 'top'].forEach(sub => {
|
||||||
|
if (typeof link[sub] === 'string')
|
||||||
|
link[sub] = {text: link[sub]};
|
||||||
|
});
|
||||||
|
|
||||||
const group = self.renderLink(nodes, link);
|
const group = self.renderLink(nodes, link);
|
||||||
if (group)
|
if (group)
|
||||||
data['g'].push(group);
|
data['g'].push(group);
|
||||||
|
|||||||
Reference in New Issue
Block a user