From ed8ddd349de06859ae646f684a670f651430f3a1 Mon Sep 17 00:00:00 2001 From: Klemek Date: Tue, 16 Jul 2019 15:03:35 +0200 Subject: [PATCH] custom icons in diagram definition --- README.md | 13 ++++++-- src/rendering.js | 46 ++++++++++++++++++++-------- test/rendering.test.js | 68 +++++++++++++++++++++++++++++++++++++----- 3 files changed, 104 insertions(+), 23 deletions(-) diff --git a/README.md b/README.md index a0092c1..0491a82 100644 --- a/README.md +++ b/README.md @@ -193,7 +193,7 @@ Array of object as following: | Key | Type | Required | Info | | --- | --- | --- | --- | | **`name`** | string | **yes** | used in links to reference nodes | -| **`icon`** | string | **yes** | name of the Font-Awesome icon of the node (see [Icon names](#icon-names)) | +| **`icon`** | string/object | **yes** | name of the Font-Awesome icon of the node (see [Icon names](#icon-names)) | | `top`, `bottom`, `left`, `right` | string or object | no | see [Sub-elements](#sub-elements) | | `color` | string | no | redefined the color | | `scale` | number | no | redefine this node icon scale | @@ -236,7 +236,7 @@ You can define a relative icon with the following: | Key | Type | Required | Info | | --- | --- | --- | --- | -| **`icon`** | string | **yes** | name of the Font-Awesome icon of the sub-element (see [Icon names](#icon-names)) | +| **`icon`** | string/object | **yes** | name of the Font-Awesome icon of the sub-element (see [Icon names](#icon-names)) | | `color` | string | no | redefine the color | | `scale` | number | no | redefine this icon scale | @@ -250,6 +250,15 @@ When you reference an icon, for example `circle`, it's searched in the `solid` f If, in this case you want the hollow circle from the regular style, just enter `regular circle` or `circle regular` instead. It's very flexible as you can copy-paste from an HTML page `far fa-circle` and it will also works. + +You can also specify a custom icon by entering the following object instead of a string: + +| Key | Type | Required | Info | +| --- | --- | --- | --- | +| **`path`** | string | **yes** | the SVG path of your icon | +| `width` | string | no | the custom width of the path (if blank, height is taken, then the height of all icons: 512) | +| `height` | string | no | the custom height of the path (if blank, width is taken) | + ### Link types Here are the accepted types and their preview : diff --git a/src/rendering.js b/src/rendering.js index ce98e12..c6c104e 100644 --- a/src/rendering.js +++ b/src/rendering.js @@ -25,7 +25,7 @@ try { * @property {string} name * @property {number} x * @property {number} y - * @property {string} icon + * @property {string|{path:string,width:number:height:number}} icon */ /** @@ -38,7 +38,12 @@ try { const SUB_DEF = { '_': 'string', 'text': 'string', - 'icon': 'string', + 'icon': { + '_': 'string', + 'path': 'string', + 'width': 'number', + 'height': 'number' + }, 'color': 'string', 'font': 'string', 'font-size': 'number', @@ -48,7 +53,12 @@ const SUB_DEF = { const NODE_DEF = { 'name': '!string', - 'icon': '!string', + 'icon': { + '_': 'string', + 'path': 'string', + 'width': 'number', + 'height': 'number' + }, 'x': '!number', 'y': '!number', 'color': 'string', @@ -110,16 +120,25 @@ module.exports = (options) => { const self = { /** - * Find icon data from given name - * @param {string} name - * @returns {null|{path: string, width: number}} + * Find icon data from given name or data + * @param {string|{path: string, width: number, height: number}} icon + * @returns {null|{path: string, width: number, height: number}} */ - getIcon: (name) => { - if (!name || !name.trim()) + getIcon: (icon) => { + if (!icon) + return null; + + if (typeof icon === 'object') { + icon.height = icon.height || icon.width || resources.height; + icon.width = icon.width || icon.height; + return icon; + } + + if (!icon.trim()) return null; let search = utils.ezClone(resources.index); - const spl = name.trim().split(' ').map(t => t.indexOf('fa-') === 0 ? t.substr(3) : t); + const spl = icon.trim().split(' ').map(t => t.indexOf('fa-') === 0 ? t.substr(3) : t); for (let i = 0; i < spl.length; i++) { //replace fas by regular for example @@ -131,11 +150,12 @@ module.exports = (options) => { } } - name = spl[0]; + icon = spl[0]; for (let i = 0; i < search.length; i++) { - if (resources.icons[search[i]] && resources.icons[search[i]][name]) { - return resources.icons[search[i]][name]; + if (resources.icons[search[i]] && resources.icons[search[i]][icon]) { + resources.icons[search[i]][icon].height = resources.height; + return resources.icons[search[i]][icon]; } } @@ -216,7 +236,7 @@ module.exports = (options) => { }, 'g': { '_attributes': { - 'transform': `scale(${scale / resources.height} ${scale / resources.height}) translate(${-icon.width / 2} ${-resources.height / 2})`, + 'transform': `scale(${scale / icon.height} ${scale / icon.height}) translate(${-icon.width / 2} ${-icon.height / 2})`, 'stroke': (node['color'] || options['icons']['color'] || undefined), 'fill': (node['color'] || options['icons']['color'] || undefined) }, diff --git a/test/rendering.test.js b/test/rendering.test.js index 34c887c..14a3a78 100644 --- a/test/rendering.test.js +++ b/test/rendering.test.js @@ -61,56 +61,64 @@ describe('getIcon', () => { const res = rendering().getIcon('circle'); expect(res).toEqual({ path: solidCirclePath, - width: 512 + width: 512, + height: 512 }); }); test('valid circle alt name', () => { const res = rendering().getIcon('fa-circle'); expect(res).toEqual({ path: solidCirclePath, - width: 512 + width: 512, + height: 512 }); }); test('ignored other name', () => { const res = rendering().getIcon('circle server'); expect(res).toEqual({ path: solidCirclePath, - width: 512 + width: 512, + height: 512 }); }); test('forcing regular', () => { const res = rendering().getIcon('far circle'); expect(res).toEqual({ path: regularCirclePath, - width: 512 + width: 512, + height: 512 }); }); test('forcing regular 2', () => { const res = rendering().getIcon('regular circle'); expect(res).toEqual({ path: regularCirclePath, - width: 512 + width: 512, + height: 512 }); }); test('forcing regular 3', () => { const res = rendering().getIcon('circle far'); expect(res).toEqual({ path: regularCirclePath, - width: 512 + width: 512, + height: 512 }); }); test('double type', () => { const res = rendering().getIcon('circle far solid'); expect(res).toEqual({ path: regularCirclePath, - width: 512 + width: 512, + height: 512 }); }); test('double type 2', () => { const res = rendering().getIcon('circle solid far'); expect(res).toEqual({ path: solidCirclePath, - width: 512 + width: 512, + height: 512 }); }); test('forcing solid on brands icon', () => { @@ -119,6 +127,50 @@ describe('getIcon', () => { const res2 = rendering().getIcon('fas usb'); expect(res2).toBeNull(); }); + test('custom icon 1', () => { + const res = rendering().getIcon({ + path: 'hello', + width: 80, + height: 160 + }); + expect(res).toEqual({ + path: 'hello', + width: 80, + height: 160 + }); + }); + test('custom icon 2', () => { + const res = rendering().getIcon({ + path: 'hello', + width: 80 + }); + expect(res).toEqual({ + path: 'hello', + width: 80, + height: 80 + }); + }); + test('custom icon 3', () => { + const res = rendering().getIcon({ + path: 'hello', + height: 160 + }); + expect(res).toEqual({ + path: 'hello', + width: 160, + height: 160 + }); + }); + test('custom icon 4', () => { + const res = rendering().getIcon({ + path: 'hello' + }); + expect(res).toEqual({ + path: 'hello', + width: 512, + height: 512 + }); + }); }); describe('getLinkPath (non-regression)', () => {