Skip to content

Commit 07fa382

Browse files
authored
Merge pull request #2762 from vdh/create-icon-revamp
Revamp of mode bar's createIcon
2 parents a2349ce + 586ff25 commit 07fa382

File tree

4 files changed

+206
-51
lines changed

4 files changed

+206
-51
lines changed

build/ploticon.js

+40-40
Original file line numberDiff line numberDiff line change
@@ -3,122 +3,122 @@
33
module.exports = {
44
'undo': {
55
'width': 857.1,
6+
'height': 1000,
67
'path': 'm857 350q0-87-34-166t-91-137-137-92-166-34q-96 0-183 41t-147 114q-4 6-4 13t5 11l76 77q6 5 14 5 9-1 13-7 41-53 100-82t126-29q58 0 110 23t92 61 61 91 22 111-22 111-61 91-92 61-110 23q-55 0-105-20t-90-57l77-77q17-16 8-38-10-23-33-23h-250q-15 0-25 11t-11 25v250q0 24 22 33 22 10 39-8l72-72q60 57 137 88t159 31q87 0 166-34t137-92 91-137 34-166z',
7-
'ascent': 850,
8-
'descent': -150
8+
'transform': 'matrix(1 0 0 -1 0 850)'
99
},
1010
'home': {
1111
'width': 928.6,
12+
'height': 1000,
1213
'path': 'm786 296v-267q0-15-11-26t-25-10h-214v214h-143v-214h-214q-15 0-25 10t-11 26v267q0 1 0 2t0 2l321 264 321-264q1-1 1-4z m124 39l-34-41q-5-5-12-6h-2q-7 0-12 3l-386 322-386-322q-7-4-13-4-7 2-12 7l-35 41q-4 5-3 13t6 12l401 334q18 15 42 15t43-15l136-114v109q0 8 5 13t13 5h107q8 0 13-5t5-13v-227l122-102q5-5 6-12t-4-13z',
13-
'ascent': 850,
14-
'descent': -150
14+
'transform': 'matrix(1 0 0 -1 0 850)'
1515
},
1616
'camera-retro': {
1717
'width': 1000,
18+
'height': 1000,
1819
'path': 'm518 386q0 8-5 13t-13 5q-37 0-63-27t-26-63q0-8 5-13t13-5 12 5 5 13q0 23 16 38t38 16q8 0 13 5t5 13z m125-73q0-59-42-101t-101-42-101 42-42 101 42 101 101 42 101-42 42-101z m-572-320h858v71h-858v-71z m643 320q0 89-62 152t-152 62-151-62-63-152 63-151 151-63 152 63 62 151z m-571 358h214v72h-214v-72z m-72-107h858v143h-462l-36-71h-360v-72z m929 143v-714q0-30-21-51t-50-21h-858q-29 0-50 21t-21 51v714q0 30 21 51t50 21h858q29 0 50-21t21-51z',
19-
'ascent': 850,
20-
'descent': -150
20+
'transform': 'matrix(1 0 0 -1 0 850)'
2121
},
2222
'zoombox': {
2323
'width': 1000,
24+
'height': 1000,
2425
'path': 'm1000-25l-250 251c40 63 63 138 63 218 0 224-182 406-407 406-224 0-406-182-406-406s183-406 407-406c80 0 155 22 218 62l250-250 125 125z m-812 250l0 438 437 0 0-438-437 0z m62 375l313 0 0-312-313 0 0 312z',
25-
'ascent': 850,
26-
'descent': -150
26+
'transform': 'matrix(1 0 0 -1 0 850)'
2727
},
2828
'pan': {
2929
'width': 1000,
30+
'height': 1000,
3031
'path': 'm1000 350l-187 188 0-125-250 0 0 250 125 0-188 187-187-187 125 0 0-250-250 0 0 125-188-188 186-187 0 125 252 0 0-250-125 0 187-188 188 188-125 0 0 250 250 0 0-126 187 188z',
31-
'ascent': 850,
32-
'descent': -150
32+
'transform': 'matrix(1 0 0 -1 0 850)'
3333
},
3434
'zoom_plus': {
3535
'width': 1000,
36+
'height': 1000,
3637
'path': 'm1 787l0-875 875 0 0 875-875 0z m687-500l-187 0 0-187-125 0 0 187-188 0 0 125 188 0 0 187 125 0 0-187 187 0 0-125z',
37-
'ascent': 850,
38-
'descent': -150
38+
'transform': 'matrix(1 0 0 -1 0 850)'
3939
},
4040
'zoom_minus': {
4141
'width': 1000,
42+
'height': 1000,
4243
'path': 'm0 788l0-876 875 0 0 876-875 0z m688-500l-500 0 0 125 500 0 0-125z',
43-
'ascent': 850,
44-
'descent': -150
44+
'transform': 'matrix(1 0 0 -1 0 850)'
4545
},
4646
'autoscale': {
4747
'width': 1000,
48+
'height': 1000,
4849
'path': 'm250 850l-187 0-63 0 0-62 0-188 63 0 0 188 187 0 0 62z m688 0l-188 0 0-62 188 0 0-188 62 0 0 188 0 62-62 0z m-875-938l0 188-63 0 0-188 0-62 63 0 187 0 0 62-187 0z m875 188l0-188-188 0 0-62 188 0 62 0 0 62 0 188-62 0z m-125 188l-1 0-93-94-156 156 156 156 92-93 2 0 0 250-250 0 0-2 93-92-156-156-156 156 94 92 0 2-250 0 0-250 0 0 93 93 157-156-157-156-93 94 0 0 0-250 250 0 0 0-94 93 156 157 156-157-93-93 0 0 250 0 0 250z',
49-
'ascent': 850,
50-
'descent': -150
50+
'transform': 'matrix(1 0 0 -1 0 850)'
5151
},
5252
'tooltip_basic': {
5353
'width': 1500,
54+
'height': 1000,
5455
'path': 'm375 725l0 0-375-375 375-374 0-1 1125 0 0 750-1125 0z',
55-
'ascent': 850,
56-
'descent': -150
56+
'transform': 'matrix(1 0 0 -1 0 850)'
5757
},
5858
'tooltip_compare': {
5959
'width': 1125,
60+
'height': 1000,
6061
'path': 'm187 786l0 2-187-188 188-187 0 0 937 0 0 373-938 0z m0-499l0 1-187-188 188-188 0 0 937 0 0 376-938-1z',
61-
'ascent': 850,
62-
'descent': -150
62+
'transform': 'matrix(1 0 0 -1 0 850)'
6363
},
6464
'plotlylogo': {
6565
'width': 1542,
66+
'height': 1000,
6667
'path': 'm0-10h182v-140h-182v140z m228 146h183v-286h-183v286z m225 714h182v-1000h-182v1000z m225-285h182v-715h-182v715z m225 142h183v-857h-183v857z m231-428h182v-429h-182v429z m225-291h183v-138h-183v138z',
67-
'ascent': 850,
68-
'descent': -150
68+
'transform': 'matrix(1 0 0 -1 0 850)'
6969
},
7070
'z-axis': {
7171
'width': 1000,
72+
'height': 1000,
7273
'path': 'm833 5l-17 108v41l-130-65 130-66c0 0 0 38 0 39 0-1 36-14 39-25 4-15-6-22-16-30-15-12-39-16-56-20-90-22-187-23-279-23-261 0-341 34-353 59 3 60 228 110 228 110-140-8-351-35-351-116 0-120 293-142 474-142 155 0 477 22 477 142 0 50-74 79-163 96z m-374 94c-58-5-99-21-99-40 0-24 65-43 144-43 79 0 143 19 143 43 0 19-42 34-98 40v216h87l-132 135-133-135h88v-216z m167 515h-136v1c16 16 31 34 46 52l84 109v54h-230v-71h124v-1c-16-17-28-32-44-51l-89-114v-51h245v72z',
73-
'ascent': 850,
74-
'descent': -150
74+
'transform': 'matrix(1 0 0 -1 0 850)'
7575
},
7676
'3d_rotate': {
7777
'width': 1000,
78+
'height': 1000,
7879
'path': 'm922 660c-5 4-9 7-14 11-359 263-580-31-580-31l-102 28 58-400c0 1 1 1 2 2 118 108 351 249 351 249s-62 27-100 42c88 83 222 183 347 122 16-8 30-17 44-27-2 1-4 2-6 4z m36-329c0 0 64 229-88 296-62 27-124 14-175-11 157-78 225-208 249-266 8-19 11-31 11-31 2 5 6 15 11 32-5-13-8-20-8-20z m-775-239c70-31 117-50 198-32-121 80-199 346-199 346l-96-15-58-12c0 0 55-226 155-287z m603 133l-317-139c0 0 4-4 19-14 7-5 24-15 24-15s-177-147-389 4c235-287 536-112 536-112l31-22 100 299-4-1z m-298-153c6-4 14-9 24-15 0 0-17 10-24 15z',
79-
'ascent': 850,
80-
'descent': -150
80+
'transform': 'matrix(1 0 0 -1 0 850)'
8181
},
8282
'camera': {
8383
'width': 1000,
84+
'height': 1000,
8485
'path': 'm500 450c-83 0-150-67-150-150 0-83 67-150 150-150 83 0 150 67 150 150 0 83-67 150-150 150z m400 150h-120c-16 0-34 13-39 29l-31 93c-6 15-23 28-40 28h-340c-16 0-34-13-39-28l-31-94c-6-15-23-28-40-28h-120c-55 0-100-45-100-100v-450c0-55 45-100 100-100h800c55 0 100 45 100 100v450c0 55-45 100-100 100z m-400-550c-138 0-250 112-250 250 0 138 112 250 250 250 138 0 250-112 250-250 0-138-112-250-250-250z m365 380c-19 0-35 16-35 35 0 19 16 35 35 35 19 0 35-16 35-35 0-19-16-35-35-35z',
85-
'ascent': 850,
86-
'descent': -150
86+
'transform': 'matrix(1 0 0 -1 0 850)'
8787
},
8888
'movie': {
8989
'width': 1000,
90+
'height': 1000,
9091
'path': 'm938 413l-188-125c0 37-17 71-44 94 64 38 107 107 107 187 0 121-98 219-219 219-121 0-219-98-219-219 0-61 25-117 66-156h-115c30 33 49 76 49 125 0 103-84 187-187 187s-188-84-188-187c0-57 26-107 65-141-38-22-65-62-65-109v-250c0-70 56-126 125-126h500c69 0 125 56 125 126l188-126c34 0 62 28 62 63v375c0 35-28 63-62 63z m-750 0c-69 0-125 56-125 125s56 125 125 125 125-56 125-125-56-125-125-125z m406-1c-87 0-157 70-157 157 0 86 70 156 157 156s156-70 156-156-70-157-156-157z',
91-
'ascent': 850,
92-
'descent': -150
92+
'transform': 'matrix(1 0 0 -1 0 850)'
9393
},
9494
'question': {
9595
'width': 857.1,
96+
'height': 1000,
9697
'path': 'm500 82v107q0 8-5 13t-13 5h-107q-8 0-13-5t-5-13v-107q0-8 5-13t13-5h107q8 0 13 5t5 13z m143 375q0 49-31 91t-77 65-95 23q-136 0-207-119-9-14 4-24l74-55q4-4 10-4 9 0 14 7 30 38 48 51 19 14 48 14 27 0 48-15t21-33q0-21-11-34t-38-25q-35-16-65-48t-29-70v-20q0-8 5-13t13-5h107q8 0 13 5t5 13q0 10 12 27t30 28q18 10 28 16t25 19 25 27 16 34 7 45z m214-107q0-117-57-215t-156-156-215-58-216 58-155 156-58 215 58 215 155 156 216 58 215-58 156-156 57-215z',
97-
'ascent': 850,
98-
'descent': -150
98+
'transform': 'matrix(1 0 0 -1 0 850)'
9999
},
100100
'disk': {
101101
'width': 857.1,
102+
'height': 1000,
102103
'path': 'm214-7h429v214h-429v-214z m500 0h72v500q0 8-6 21t-11 20l-157 156q-5 6-19 12t-22 5v-232q0-22-15-38t-38-16h-322q-22 0-37 16t-16 38v232h-72v-714h72v232q0 22 16 38t37 16h465q22 0 38-16t15-38v-232z m-214 518v178q0 8-5 13t-13 5h-107q-7 0-13-5t-5-13v-178q0-8 5-13t13-5h107q7 0 13 5t5 13z m357-18v-518q0-22-15-38t-38-16h-750q-23 0-38 16t-16 38v750q0 22 16 38t38 16h517q23 0 50-12t42-26l156-157q16-15 27-42t11-49z',
103-
'ascent': 850,
104-
'descent': -150
104+
'transform': 'matrix(1 0 0 -1 0 850)'
105105
},
106106
'lasso': {
107107
'width': 1031,
108+
'height': 1000,
108109
'path': 'm1018 538c-36 207-290 336-568 286-277-48-473-256-436-463 10-57 36-108 76-151-13-66 11-137 68-183 34-28 75-41 114-42l-55-70 0 0c-2-1-3-2-4-3-10-14-8-34 5-45 14-11 34-8 45 4 1 1 2 3 2 5l0 0 113 140c16 11 31 24 45 40 4 3 6 7 8 11 48-3 100 0 151 9 278 48 473 255 436 462z m-624-379c-80 14-149 48-197 96 42 42 109 47 156 9 33-26 47-66 41-105z m-187-74c-19 16-33 37-39 60 50-32 109-55 174-68-42-25-95-24-135 8z m360 75c-34-7-69-9-102-8 8 62-16 128-68 170-73 59-175 54-244-5-9 20-16 40-20 61-28 159 121 317 333 354s407-60 434-217c28-159-121-318-333-355z',
109-
'ascent': 850,
110-
'descent': -150
110+
'transform': 'matrix(1 0 0 -1 0 850)'
111111
},
112112
'selectbox': {
113113
'width': 1000,
114+
'height': 1000,
114115
'path': 'm0 850l0-143 143 0 0 143-143 0z m286 0l0-143 143 0 0 143-143 0z m285 0l0-143 143 0 0 143-143 0z m286 0l0-143 143 0 0 143-143 0z m-857-286l0-143 143 0 0 143-143 0z m857 0l0-143 143 0 0 143-143 0z m-857-285l0-143 143 0 0 143-143 0z m857 0l0-143 143 0 0 143-143 0z m-857-286l0-143 143 0 0 143-143 0z m286 0l0-143 143 0 0 143-143 0z m285 0l0-143 143 0 0 143-143 0z m286 0l0-143 143 0 0 143-143 0z',
115-
'ascent': 850,
116-
'descent': -150
116+
'transform': 'matrix(1 0 0 -1 0 850)'
117117
},
118118
'spikeline': {
119119
'width': 1000,
120+
'height': 1000,
120121
'path': 'M512 409c0-57-46-104-103-104-57 0-104 47-104 104 0 57 47 103 104 103 57 0 103-46 103-103z m-327-39l92 0 0 92-92 0z m-185 0l92 0 0 92-92 0z m370-186l92 0 0 93-92 0z m0-184l92 0 0 92-92 0z',
121-
'ascent': 850,
122-
'descent': -150
122+
'transform': 'matrix(1.5 0 0 -1.5 0 850)'
123123
}
124124
};

src/components/modebar/modebar.js

+21-8
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
'use strict';
1111

1212
var d3 = require('d3');
13+
var isNumeric = require('fast-isnumeric');
1314

1415
var Lib = require('../../lib');
1516
var Icons = require('../../../build/ploticon');
@@ -155,7 +156,13 @@ proto.createButton = function(config) {
155156
button.setAttribute('data-toggle', config.toggle || false);
156157
if(config.toggle) d3.select(button).classed('active', true);
157158

158-
button.appendChild(this.createIcon(config.icon || Icons.question, config.name));
159+
var icon = config.icon;
160+
if(typeof icon === 'function') {
161+
button.appendChild(icon());
162+
}
163+
else {
164+
button.appendChild(this.createIcon(icon || Icons.question));
165+
}
159166
button.setAttribute('data-gravity', config.gravity || 'n');
160167

161168
return button;
@@ -168,8 +175,10 @@ proto.createButton = function(config) {
168175
* @Param {string} thisIcon.path
169176
* @Return {HTMLelement}
170177
*/
171-
proto.createIcon = function(thisIcon, name) {
172-
var iconHeight = thisIcon.ascent - thisIcon.descent,
178+
proto.createIcon = function(thisIcon) {
179+
var iconHeight = isNumeric(thisIcon.height) ?
180+
Number(thisIcon.height) :
181+
thisIcon.ascent - thisIcon.descent,
173182
svgNS = 'http://www.w3.org/2000/svg',
174183
icon = document.createElementNS(svgNS, 'svg'),
175184
path = document.createElementNS(svgNS, 'path');
@@ -178,12 +187,16 @@ proto.createIcon = function(thisIcon, name) {
178187
icon.setAttribute('width', (thisIcon.width / iconHeight) + 'em');
179188
icon.setAttribute('viewBox', [0, 0, thisIcon.width, iconHeight].join(' '));
180189

181-
var transform = name === 'toggleSpikelines' ?
182-
'matrix(1.5 0 0 -1.5 0 ' + thisIcon.ascent + ')' :
183-
'matrix(1 0 0 -1 0 ' + thisIcon.ascent + ')';
184-
185190
path.setAttribute('d', thisIcon.path);
186-
path.setAttribute('transform', transform);
191+
192+
if(thisIcon.transform) {
193+
path.setAttribute('transform', thisIcon.transform);
194+
}
195+
else if(thisIcon.ascent !== undefined) {
196+
// Legacy icon transform calculation
197+
path.setAttribute('transform', 'matrix(1 0 0 -1 0 ' + thisIcon.ascent + ')');
198+
}
199+
187200
icon.appendChild(path);
188201

189202
return icon;

tasks/util/pull_font_svg.js

+8-3
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,16 @@ module.exports = function pullFontSVG(data, pathOut) {
1515
chars = {};
1616

1717
font_obj.glyph.forEach(function(glyph) {
18-
chars[glyph.$['glyph-name']] = {
18+
var name = glyph.$['glyph-name'],
19+
transform = name === 'spikeline' ?
20+
'matrix(1.5 0 0 -1.5 0 ' + ascent + ')' :
21+
'matrix(1 0 0 -1 0 ' + ascent + ')';
22+
23+
chars[name] = {
1924
width: Number(glyph.$['horiz-adv-x']) || default_width,
25+
height: ascent - descent,
2026
path: glyph.$.d,
21-
ascent: ascent,
22-
descent: descent
27+
transform: transform,
2328
};
2429
});
2530

test/jasmine/tests/modebar_test.js

+137
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,143 @@ describe('ModeBar', function() {
136136
]]);
137137
expect(checkBtnAttr(modeBar, 0, 'data-title')).toEqual('0');
138138
});
139+
140+
describe('creates a custom button', function() {
141+
function getIconSvg(modeBar) {
142+
if(!modeBar || !modeBar.buttonElements || !modeBar.buttonElements.length > 0) {
143+
return undefined;
144+
}
145+
var button = modeBar.buttonElements[0];
146+
return d3.select(button).select('svg');
147+
}
148+
149+
it('with a Plotly icon', function() {
150+
var modeBar = createModeBar(getMockGraphInfo(), [[
151+
{
152+
name: 'some button',
153+
click: noop,
154+
icon: Plotly.Icons.home
155+
}
156+
]]);
157+
158+
var svg = getIconSvg(modeBar);
159+
expect(svg).toBeDefined();
160+
var path = svg.select('path');
161+
expect(path.attr('d')).toBeDefined();
162+
expect(path.attr('transform')).toBeDefined();
163+
});
164+
165+
it('with a custom icon', function() {
166+
var modeBar = createModeBar(getMockGraphInfo(), [[
167+
{
168+
name: 'some button',
169+
click: noop,
170+
icon: {
171+
width: 1000,
172+
height: 1000,
173+
path: 'M0,-150 L1000,-150 L500,850 L0,-150 Z',
174+
transform: 'matrix(1 0 0 -1 0 850)',
175+
}
176+
}
177+
]]);
178+
179+
var svg = getIconSvg(modeBar);
180+
expect(svg).toBeDefined();
181+
expect(svg.attr('viewBox')).toBe('0 0 1000 1000');
182+
var path = svg.select('path');
183+
expect(path.attr('d')).toBeDefined();
184+
expect(path.attr('transform')).toEqual('matrix(1 0 0 -1 0 850)');
185+
});
186+
187+
it('with a custom icon with no transform', function() {
188+
var modeBar = createModeBar(getMockGraphInfo(), [[
189+
{
190+
name: 'some button',
191+
click: noop,
192+
icon: {
193+
width: 1000,
194+
height: 1000,
195+
path: 'M500,0 L1000,1000 L0,1000 L500,0 Z',
196+
}
197+
}
198+
]]);
199+
200+
var svg = getIconSvg(modeBar);
201+
expect(svg).toBeDefined();
202+
expect(svg.attr('viewBox')).toBe('0 0 1000 1000');
203+
var path = svg.select('path');
204+
expect(path.attr('d')).toBeDefined();
205+
expect(path.attr('transform')).toBeNull();
206+
});
207+
208+
it('with a custom icon created by a function', function() {
209+
var modeBar = createModeBar(getMockGraphInfo(), [[
210+
{
211+
name: 'some button',
212+
click: noop,
213+
icon: function() {
214+
var xmlns = 'http://www.w3.org/2000/svg';
215+
var icon = document.createElementNS(xmlns, 'svg');
216+
icon.setAttribute('viewBox', '0 0 1000 1000');
217+
icon.setAttribute('height', '1em');
218+
icon.setAttribute('width', '1em');
219+
icon.setAttribute('class', 'custom-svg');
220+
var path = document.createElementNS(xmlns, 'path');
221+
path.setAttribute('d', 'M500,0 L1000,1000 L0,1000 L500,0 Z');
222+
icon.appendChild(path);
223+
return icon;
224+
}
225+
}
226+
]]);
227+
228+
var svg = getIconSvg(modeBar);
229+
expect(svg).toBeDefined();
230+
expect(svg.attr('viewBox')).toBe('0 0 1000 1000');
231+
expect(svg.attr('class')).toBe('custom-svg');
232+
var path = svg.select('path');
233+
expect(path.attr('d')).toBeDefined();
234+
expect(path.attr('transform')).toBeNull();
235+
});
236+
237+
it('with a legacy icon config', function() {
238+
var modeBar = createModeBar(getMockGraphInfo(), [[
239+
{
240+
name: 'some button',
241+
click: noop,
242+
icon: {
243+
width: 1000,
244+
path: 'M0,-150 L1000,-150 L500,850 L0,-150 Z',
245+
ascent: 850,
246+
descent: -150
247+
}
248+
}
249+
]]);
250+
251+
var svg = getIconSvg(modeBar);
252+
expect(svg).toBeDefined();
253+
expect(svg.attr('viewBox')).toBe('0 0 1000 1000');
254+
var path = svg.select('path');
255+
expect(path.attr('d')).toBeDefined();
256+
expect(path.attr('transform')).toEqual('matrix(1 0 0 -1 0 850)');
257+
});
258+
259+
it('with the spikeline icon', function() {
260+
var modeBar = createModeBar(getMockGraphInfo(), [[
261+
{
262+
name: 'some button',
263+
click: noop,
264+
icon: Plotly.Icons.spikeline
265+
}
266+
]]);
267+
268+
var svg = getIconSvg(modeBar);
269+
expect(svg).toBeDefined();
270+
expect(svg.attr('viewBox')).toBe('0 0 1000 1000');
271+
var path = svg.select('path');
272+
expect(path.attr('d')).toBeDefined();
273+
expect(path.attr('transform')).toEqual('matrix(1.5 0 0 -1.5 0 850)');
274+
});
275+
});
139276
});
140277

141278
describe('modeBar.removeAllButtons', function() {

0 commit comments

Comments
 (0)