Skip to content

Commit 88d48c5

Browse files
authored
Merge pull request #3068 from plotly/3063-modebar-style
modeBar styling
2 parents a285aeb + db9edd1 commit 88d48c5

File tree

11 files changed

+293
-49
lines changed

11 files changed

+293
-49
lines changed

build/plotcss.js

+7-7
Original file line numberDiff line numberDiff line change
@@ -31,21 +31,21 @@ var rules = {
3131
"X .cursor-n-resize": "cursor:n-resize;",
3232
"X .cursor-ne-resize": "cursor:ne-resize;",
3333
"X .cursor-grab": "cursor:-webkit-grab;cursor:grab;",
34-
"X .modebar": "position:absolute;top:2px;right:2px;z-index:1001;background:rgba(255,255,255,0.7);",
34+
"X .modebar": "position:absolute;top:2px;right:2px;z-index:1001;",
3535
"X .modebar--hover": "opacity:0;-webkit-transition:opacity 0.3s ease 0s;-moz-transition:opacity 0.3s ease 0s;-ms-transition:opacity 0.3s ease 0s;-o-transition:opacity 0.3s ease 0s;transition:opacity 0.3s ease 0s;",
3636
"X:hover .modebar--hover": "opacity:1;",
3737
"X .modebar-group": "float:left;display:inline-block;box-sizing:border-box;margin-left:8px;position:relative;vertical-align:middle;white-space:nowrap;",
38-
"X .modebar-group:first-child": "margin-left:0px;",
39-
"X .modebar-btn": "position:relative;font-size:16px;padding:3px 4px;cursor:pointer;line-height:normal;box-sizing:border-box;",
38+
"X .modebar-btn": "position:relative;font-size:16px;padding:3px 4px;height:22px;cursor:pointer;line-height:normal;box-sizing:border-box;",
4039
"X .modebar-btn svg": "position:relative;top:2px;",
41-
"X .modebar-btn path": "fill:rgba(0,31,95,0.3);",
42-
"X .modebar-btn.active path,X .modebar-btn:hover path": "fill:rgba(0,22,72,0.5);",
43-
"X .modebar-btn.modebar-btn--logo": "padding:3px 1px;",
44-
"X .modebar-btn.modebar-btn--logo path": "fill:#447adb !important;",
40+
"X .modebar.vertical": "top:-1px;",
41+
"X .modebar.vertical .modebar-group": "display:block;float:none;margin-left:0px;margin-bottom:8px;",
42+
"X .modebar.vertical .modebar-group .modebar-btn": "display:block;text-align:center;",
4543
"X [data-title]:before,X [data-title]:after": "position:absolute;-webkit-transform:translate3d(0, 0, 0);-moz-transform:translate3d(0, 0, 0);-ms-transform:translate3d(0, 0, 0);-o-transform:translate3d(0, 0, 0);transform:translate3d(0, 0, 0);display:none;opacity:0;z-index:1001;pointer-events:none;top:110%;right:50%;",
4644
"X [data-title]:hover:before,X [data-title]:hover:after": "display:block;opacity:1;",
4745
"X [data-title]:before": "content:'';position:absolute;background:transparent;border:6px solid transparent;z-index:1002;margin-top:-12px;border-bottom-color:#69738a;margin-right:-6px;",
4846
"X [data-title]:after": "content:attr(data-title);background:#69738a;color:white;padding:8px 10px;font-size:12px;line-height:12px;white-space:nowrap;margin-right:-18px;border-radius:2px;",
47+
"X .vertical [data-title]:before,X .vertical [data-title]:after": "top:0%;right:200%;",
48+
"X .vertical [data-title]:before": "border:6px solid transparent;border-left-color:#69738a;margin-top:8px;margin-right:-30px;",
4949
"X .select-outline": "fill:none;stroke-width:1;shape-rendering:crispEdges;",
5050
"X .select-outline-1": "stroke:white;",
5151
"X .select-outline-2": "stroke:black;stroke-dasharray:2px 2px;",

build/ploticon.js

+6-2
Original file line numberDiff line numberDiff line change
@@ -32,13 +32,13 @@ module.exports = {
3232
'transform': 'matrix(1 0 0 -1 0 850)'
3333
},
3434
'zoom_plus': {
35-
'width': 1000,
35+
'width': 875,
3636
'height': 1000,
3737
'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',
3838
'transform': 'matrix(1 0 0 -1 0 850)'
3939
},
4040
'zoom_minus': {
41-
'width': 1000,
41+
'width': 875,
4242
'height': 1000,
4343
'path': 'm0 788l0-876 875 0 0 876-875 0z m688-500l-500 0 0 125 500 0 0-125z',
4444
'transform': 'matrix(1 0 0 -1 0 850)'
@@ -120,5 +120,9 @@ module.exports = {
120120
'height': 1000,
121121
'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',
122122
'transform': 'matrix(1.5 0 0 -1.5 0 850)'
123+
},
124+
'newplotlylogo': {
125+
'name': 'newplotlylogo',
126+
'svg': '<svg xmlns=\'http://www.w3.org/2000/svg\' viewBox=\'0 0 132 132\'><defs><style>.cls-1 {fill: #119dff;} .cls-2 {fill: #25fefd;} .cls-3 {fill: #fff;}</style></defs><title>plotly-logomark</title><g id=\'symbol\'><rect class=\'cls-1\' width=\'132\' height=\'132\' rx=\'6\' ry=\'6\'/><circle class=\'cls-2\' cx=\'78\' cy=\'54\' r=\'6\'/><circle class=\'cls-2\' cx=\'102\' cy=\'30\' r=\'6\'/><circle class=\'cls-2\' cx=\'78\' cy=\'30\' r=\'6\'/><circle class=\'cls-2\' cx=\'54\' cy=\'30\' r=\'6\'/><circle class=\'cls-2\' cx=\'30\' cy=\'30\' r=\'6\'/><circle class=\'cls-2\' cx=\'30\' cy=\'54\' r=\'6\'/><path class=\'cls-3\' d=\'M30,72a6,6,0,0,0-6,6v24a6,6,0,0,0,12,0V78A6,6,0,0,0,30,72Z\'/><path class=\'cls-3\' d=\'M78,72a6,6,0,0,0-6,6v24a6,6,0,0,0,12,0V78A6,6,0,0,0,78,72Z\'/><path class=\'cls-3\' d=\'M54,48a6,6,0,0,0-6,6v48a6,6,0,0,0,12,0V54A6,6,0,0,0,54,48Z\'/><path class=\'cls-3\' d=\'M102,48a6,6,0,0,0-6,6v48a6,6,0,0,0,12,0V54A6,6,0,0,0,102,48Z\'/></g></svg>'
123127
}
124128
};

src/components/modebar/modebar.js

+49-16
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ var isNumeric = require('fast-isnumeric');
1414

1515
var Lib = require('../../lib');
1616
var Icons = require('../../../build/ploticon');
17-
17+
var Parser = new DOMParser();
1818

1919
/**
2020
* UI controller for interactive plots
@@ -45,13 +45,29 @@ var proto = ModeBar.prototype;
4545
proto.update = function(graphInfo, buttons) {
4646
this.graphInfo = graphInfo;
4747

48-
var context = this.graphInfo._context;
48+
var context = this.graphInfo._context,
49+
fullLayout = this.graphInfo._fullLayout,
50+
modeBarId = 'modebar-' + fullLayout._uid;
51+
52+
this.element.setAttribute('id', modeBarId);
53+
this._uid = modeBarId;
4954

5055
if(context.displayModeBar === 'hover') {
5156
this.element.className = 'modebar modebar--hover';
5257
}
5358
else this.element.className = 'modebar';
5459

60+
if(fullLayout.modebar.orientation === 'v') {
61+
this.element.className += ' vertical';
62+
buttons = buttons.reverse();
63+
}
64+
65+
Lib.deleteRelatedStyleRule(modeBarId);
66+
Lib.addRelatedStyleRule(modeBarId, '#' + modeBarId, 'background-color: ' + fullLayout.modebar.bgcolor);
67+
Lib.addRelatedStyleRule(modeBarId, '#' + modeBarId + ' .modebar-btn .icon path', 'fill: ' + fullLayout.modebar.color);
68+
Lib.addRelatedStyleRule(modeBarId, '#' + modeBarId + ' .modebar-btn:hover .icon path', 'fill: ' + fullLayout.modebar.activecolor);
69+
Lib.addRelatedStyleRule(modeBarId, '#' + modeBarId + ' .modebar-btn.active .icon path', 'fill: ' + fullLayout.modebar.activecolor);
70+
5571
// if buttons or logo have changed, redraw modebar interior
5672
var needsNewButtons = !this.hasButtons(buttons);
5773
var needsNewLogo = (this.hasLogo !== context.displaylogo);
@@ -65,7 +81,12 @@ proto.update = function(graphInfo, buttons) {
6581
this.updateButtons(buttons);
6682

6783
if(context.displaylogo) {
68-
this.element.appendChild(this.getLogo());
84+
if(fullLayout.modebar.orientation === 'v') {
85+
this.element.prepend(this.getLogo());
86+
} else {
87+
this.element.appendChild(this.getLogo());
88+
}
89+
6990
this.hasLogo = true;
7091
}
7192
}
@@ -173,31 +194,42 @@ proto.createButton = function(config) {
173194
* @Param {object} thisIcon
174195
* @Param {number} thisIcon.width
175196
* @Param {string} thisIcon.path
197+
* @Param {string} thisIcon.color
176198
* @Return {HTMLelement}
177199
*/
178200
proto.createIcon = function(thisIcon) {
179201
var iconHeight = isNumeric(thisIcon.height) ?
180202
Number(thisIcon.height) :
181203
thisIcon.ascent - thisIcon.descent,
182204
svgNS = 'http://www.w3.org/2000/svg',
183-
icon = document.createElementNS(svgNS, 'svg'),
184-
path = document.createElementNS(svgNS, 'path');
205+
icon;
185206

186-
icon.setAttribute('height', '1em');
187-
icon.setAttribute('width', (thisIcon.width / iconHeight) + 'em');
188-
icon.setAttribute('viewBox', [0, 0, thisIcon.width, iconHeight].join(' '));
207+
if(thisIcon.path) {
208+
icon = document.createElementNS(svgNS, 'svg');
209+
icon.setAttribute('viewBox', [0, 0, thisIcon.width, iconHeight].join(' '));
210+
icon.setAttribute('class', 'icon');
211+
212+
var path = document.createElementNS(svgNS, 'path');
213+
path.setAttribute('d', thisIcon.path);
189214

190-
path.setAttribute('d', thisIcon.path);
215+
if(thisIcon.transform) {
216+
path.setAttribute('transform', thisIcon.transform);
217+
}
218+
else if(thisIcon.ascent !== undefined) {
219+
// Legacy icon transform calculation
220+
path.setAttribute('transform', 'matrix(1 0 0 -1 0 ' + thisIcon.ascent + ')');
221+
}
191222

192-
if(thisIcon.transform) {
193-
path.setAttribute('transform', thisIcon.transform);
223+
icon.appendChild(path);
194224
}
195-
else if(thisIcon.ascent !== undefined) {
196-
// Legacy icon transform calculation
197-
path.setAttribute('transform', 'matrix(1 0 0 -1 0 ' + thisIcon.ascent + ')');
225+
226+
if(thisIcon.svg) {
227+
var svgDoc = Parser.parseFromString(thisIcon.svg, 'application/xml');
228+
icon = svgDoc.childNodes[0];
198229
}
199230

200-
icon.appendChild(path);
231+
icon.setAttribute('height', '1em');
232+
icon.setAttribute('width', '1em');
201233

202234
return icon;
203235
};
@@ -272,7 +304,7 @@ proto.getLogo = function() {
272304
a.setAttribute('data-title', Lib._(this.graphInfo, 'Produced with Plotly'));
273305
a.className = 'modebar-btn plotlyjsicon modebar-btn--logo';
274306

275-
a.appendChild(this.createIcon(Icons.plotlylogo));
307+
a.appendChild(this.createIcon(Icons.newplotlylogo));
276308

277309
group.appendChild(a);
278310
return group;
@@ -288,6 +320,7 @@ proto.removeAllButtons = function() {
288320

289321
proto.destroy = function() {
290322
Lib.removeElement(this.container.querySelector('.modebar'));
323+
Lib.deleteRelatedStyleRule(this._uid);
291324
};
292325

293326
function createModeBar(gd, buttons) {

src/css/_modebar.scss

+13-15
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
top: 2px;
44
right: 2px;
55
z-index: 1001;
6-
background: rgba(255,255,255,0.7);
76
}
87

98
.modebar--hover {
@@ -23,17 +22,13 @@
2322
position: relative;
2423
vertical-align: middle;
2524
white-space: nowrap;
26-
27-
&:first-child {
28-
margin-left: 0px;
29-
}
3025
}
3126

32-
3327
.modebar-btn {
3428
position: relative;
3529
font-size: 16px;
3630
padding: 3px 4px;
31+
height: 22px;
3732
/* display: inline-block; including this breaks 3d interaction in .embed mode. Chrome bug? */
3833
cursor: pointer;
3934
line-height: normal;
@@ -44,19 +39,22 @@
4439
top: 2px;
4540
}
4641

47-
path {
48-
fill: rgba(0,31,95,0.3);
49-
}
42+
&.modebar-btn--logo {
5043

51-
&.active path, &:hover path {
52-
fill: rgba(0,22,72,0.5);
5344
}
45+
}
5446

55-
&.modebar-btn--logo {
56-
padding: 3px 1px;
47+
.modebar.vertical {
48+
top: -1px;
49+
.modebar-group {
50+
display: block;
51+
float: none;
52+
margin-left: 0px;
53+
margin-bottom: 8px;
5754

58-
path {
59-
fill: $color-brand-primary !important;
55+
.modebar-btn {
56+
display: block;
57+
text-align: center;
6058
}
6159
}
6260
}

src/css/_tooltip.scss

+16-1
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ $successColor: hsl(121, 32%, 40%) !default;
5151
opacity: 1;
5252
}
5353

54-
// Arrow
54+
// Top arrow
5555
&:before {
5656
content: '';
5757
position: absolute;
@@ -78,3 +78,18 @@ $successColor: hsl(121, 32%, 40%) !default;
7878
border-radius: 2px;
7979
}
8080
}
81+
82+
.vertical [data-title] {
83+
&:before, &:after {
84+
top: 0%;
85+
right: 200%;
86+
}
87+
88+
// Right arrow
89+
&:before {
90+
border: $arrowBorderWidth solid transparent;
91+
border-left-color: $defaultColor;
92+
margin-top: $verticalPadding;
93+
margin-right: -1 * ($arrowOffsetX + 2 * $arrowBorderWidth);
94+
}
95+
}

src/fonts/ploticon/ploticon.svg

+22-2
Loading

src/lib/index.js

+23-4
Original file line numberDiff line numberDiff line change
@@ -679,14 +679,24 @@ lib.removeElement = function(el) {
679679
* by all calls to this function
680680
*/
681681
lib.addStyleRule = function(selector, styleString) {
682-
if(!lib.styleSheet) {
683-
var style = document.createElement('style');
682+
lib.addRelatedStyleRule('global', selector, styleString);
683+
};
684+
685+
/**
686+
* for dynamically adding style rules
687+
* to a stylesheet uniquely identified by a uid
688+
*/
689+
lib.addRelatedStyleRule = function(uid, selector, styleString) {
690+
var id = 'plotly.js-style-' + uid,
691+
style = document.getElementById(id);
692+
if(!style) {
693+
style = document.createElement('style');
694+
style.setAttribute('id', id);
684695
// WebKit hack :(
685696
style.appendChild(document.createTextNode(''));
686697
document.head.appendChild(style);
687-
lib.styleSheet = style.sheet;
688698
}
689-
var styleSheet = lib.styleSheet;
699+
var styleSheet = style.sheet;
690700

691701
if(styleSheet.insertRule) {
692702
styleSheet.insertRule(selector + '{' + styleString + '}', 0);
@@ -697,6 +707,15 @@ lib.addStyleRule = function(selector, styleString) {
697707
else lib.warn('addStyleRule failed');
698708
};
699709

710+
/**
711+
* to remove from the page a stylesheet identified by a given uid
712+
*/
713+
lib.deleteRelatedStyleRule = function(uid) {
714+
var id = 'plotly.js-style-' + uid,
715+
style = document.getElementById(id);
716+
if(style) style.remove();
717+
};
718+
700719
lib.isIE = function() {
701720
return typeof window.navigator.msSaveBlob !== 'undefined';
702721
};

src/plots/layout_attributes.js

+29
Original file line numberDiff line numberDiff line change
@@ -224,5 +224,34 @@ module.exports = {
224224
'or a logo image, for example. To omit one of these items on the plot,',
225225
'make an item with matching `templateitemname` and `visible: false`.'
226226
].join(' ')
227+
},
228+
modebar: {
229+
orientation: {
230+
valType: 'enumerated',
231+
values: ['v', 'h'],
232+
dflt: 'h',
233+
role: 'info',
234+
editType: 'modebar',
235+
description: 'Sets the orientation of the modebar.'
236+
},
237+
bgcolor: {
238+
valType: 'color',
239+
role: 'style',
240+
editType: 'modebar',
241+
description: 'Sets the background color of the modebar.'
242+
},
243+
color: {
244+
valType: 'color',
245+
role: 'style',
246+
editType: 'modebar',
247+
description: 'Sets the color of the icons in the modebar.'
248+
},
249+
activecolor: {
250+
valType: 'color',
251+
role: 'style',
252+
editType: 'modebar',
253+
description: 'Sets the color of the active or hovered on icons in the modebar.'
254+
},
255+
editType: 'modebar'
227256
}
228257
};

src/plots/plots.js

+6
Original file line numberDiff line numberDiff line change
@@ -1337,6 +1337,12 @@ plots.supplyLayoutGlobalDefaults = function(layoutIn, layoutOut, formatObj) {
13371337

13381338
coerce('datarevision');
13391339

1340+
coerce('modebar.orientation');
1341+
coerce('modebar.bgcolor', Color.addOpacity(layoutOut.paper_bgcolor, 0.5));
1342+
var modebarDefaultColor = Color.contrast(Color.rgb(layoutOut.modebar.bgcolor));
1343+
coerce('modebar.color', Color.addOpacity(modebarDefaultColor, 0.3));
1344+
coerce('modebar.activecolor', Color.addOpacity(modebarDefaultColor, 0.7));
1345+
13401346
Registry.getComponentMethod(
13411347
'calendars',
13421348
'handleDefaults'

0 commit comments

Comments
 (0)