plotly.js 2.7.0 → 2.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. package/CHANGELOG.md +13 -0
  2. package/README.md +3 -3
  3. package/dist/README.md +26 -26
  4. package/dist/plot-schema.json +898 -407
  5. package/dist/plotly-basic.js +489 -185
  6. package/dist/plotly-basic.min.js +4 -4
  7. package/dist/plotly-cartesian.js +894 -326
  8. package/dist/plotly-cartesian.min.js +3 -3
  9. package/dist/plotly-finance.js +489 -185
  10. package/dist/plotly-finance.min.js +4 -4
  11. package/dist/plotly-geo-assets.js +2 -2
  12. package/dist/plotly-geo.js +486 -184
  13. package/dist/plotly-geo.min.js +4 -4
  14. package/dist/plotly-gl2d.js +502 -185
  15. package/dist/plotly-gl2d.min.js +2 -2
  16. package/dist/plotly-gl3d.js +486 -184
  17. package/dist/plotly-gl3d.min.js +2 -2
  18. package/dist/plotly-mapbox.js +486 -184
  19. package/dist/plotly-mapbox.min.js +2 -2
  20. package/dist/plotly-strict.js +1112 -544
  21. package/dist/plotly-strict.min.js +3 -3
  22. package/dist/plotly-with-meta.js +1201 -606
  23. package/dist/plotly.js +1166 -598
  24. package/dist/plotly.min.js +10 -10
  25. package/package.json +4 -4
  26. package/src/components/colorbar/attributes.js +29 -20
  27. package/src/components/colorbar/defaults.js +30 -8
  28. package/src/components/colorbar/draw.js +374 -128
  29. package/src/components/fx/hover.js +5 -2
  30. package/src/components/fx/hoverlabel_defaults.js +4 -2
  31. package/src/components/fx/layout_attributes.js +14 -4
  32. package/src/components/fx/layout_defaults.js +2 -0
  33. package/src/components/legend/attributes.js +7 -0
  34. package/src/components/legend/defaults.js +24 -7
  35. package/src/components/titles/index.js +8 -2
  36. package/src/plot_api/plot_api.js +1 -1
  37. package/src/plots/font_attributes.js +3 -0
  38. package/src/plots/layout_attributes.js +1 -0
  39. package/src/plots/plots.js +7 -15
  40. package/src/traces/contour/attributes.js +12 -0
  41. package/src/traces/contour/defaults.js +9 -1
  42. package/src/traces/heatmap/attributes.js +16 -0
  43. package/src/traces/heatmap/defaults.js +2 -0
  44. package/src/traces/heatmap/label_defaults.js +13 -0
  45. package/src/traces/heatmap/plot.js +203 -4
  46. package/src/traces/histogram2d/attributes.js +8 -0
  47. package/src/traces/histogram2d/defaults.js +4 -0
  48. package/src/traces/histogram2dcontour/attributes.js +3 -1
  49. package/src/traces/histogram2dcontour/defaults.js +8 -1
  50. package/src/traces/pie/calc.js +3 -1
  51. package/src/version.js +1 -1
  52. package/tasks/test_mock.js +1 -0
@@ -167,18 +167,20 @@ function makeColorBarData(gd) {
167
167
  }
168
168
 
169
169
  function drawColorBar(g, opts, gd) {
170
+ var isVertical = opts.orientation === 'v';
170
171
  var len = opts.len;
171
172
  var lenmode = opts.lenmode;
172
173
  var thickness = opts.thickness;
173
174
  var thicknessmode = opts.thicknessmode;
174
175
  var outlinewidth = opts.outlinewidth;
175
176
  var borderwidth = opts.borderwidth;
177
+ var bgcolor = opts.bgcolor;
176
178
  var xanchor = opts.xanchor;
177
179
  var yanchor = opts.yanchor;
178
180
  var xpad = opts.xpad;
179
181
  var ypad = opts.ypad;
180
182
  var optsX = opts.x;
181
- var optsY = opts.y;
183
+ var optsY = isVertical ? opts.y : 1 - opts.y;
182
184
 
183
185
  var fullLayout = gd._fullLayout;
184
186
  var gs = fullLayout._size;
@@ -209,23 +211,35 @@ function drawColorBar(g, opts, gd) {
209
211
  // when the colorbar itself is pushing the margins.
210
212
  // but then the fractional size is calculated based on the
211
213
  // actual graph size, so that the axes will size correctly.
212
- var thickPx = Math.round(thickness * (thicknessmode === 'fraction' ? gs.w : 1));
213
- var thickFrac = thickPx / gs.w;
214
- var lenPx = Math.round(len * (lenmode === 'fraction' ? gs.h : 1));
215
- var lenFrac = lenPx / gs.h;
216
- var xpadFrac = xpad / gs.w;
217
- var yExtraPx = (borderwidth + outlinewidth) / 2;
218
- var ypadFrac = ypad / gs.h;
214
+ var thickPx = Math.round(thickness * (thicknessmode === 'fraction' ? (isVertical ? gs.w : gs.h) : 1));
215
+ var thickFrac = thickPx / (isVertical ? gs.w : gs.h);
216
+ var lenPx = Math.round(len * (lenmode === 'fraction' ? (isVertical ? gs.h : gs.w) : 1));
217
+ var lenFrac = lenPx / (isVertical ? gs.h : gs.w);
219
218
 
220
219
  // x positioning: do it initially just for left anchor,
221
220
  // then fix at the end (since we don't know the width yet)
222
- var uPx = Math.round(optsX * gs.w + xpad);
221
+ var uPx = Math.round(isVertical ?
222
+ optsX * gs.w + xpad :
223
+ optsY * gs.h + ypad
224
+ );
225
+
226
+ var xRatio = {center: 0.5, right: 1}[xanchor] || 0;
227
+ var yRatio = {top: 1, middle: 0.5}[yanchor] || 0;
228
+
223
229
  // for dragging... this is getting a little muddled...
224
- var uFrac = optsX - thickFrac * ({center: 0.5, right: 1}[xanchor] || 0);
230
+ var uFrac = isVertical ?
231
+ optsX - xRatio * thickFrac :
232
+ optsY - yRatio * thickFrac;
233
+
234
+ // y/x positioning (for v/h) we can do correctly from the start
235
+ var vFrac = isVertical ?
236
+ optsY - yRatio * lenFrac :
237
+ optsX - xRatio * lenFrac;
225
238
 
226
- // y positioning we can do correctly from the start
227
- var vFrac = optsY + lenFrac * (({top: -0.5, bottom: 0.5}[yanchor] || 0) - 0.5);
228
- var vPx = Math.round(gs.h * (1 - vFrac));
239
+ var vPx = Math.round(isVertical ?
240
+ gs.h * (1 - vFrac) :
241
+ gs.w * vFrac
242
+ );
229
243
 
230
244
  // stash a few things for makeEditable
231
245
  opts._lenFrac = lenFrac;
@@ -238,12 +252,23 @@ function drawColorBar(g, opts, gd) {
238
252
 
239
253
  // position can't go in through supplyDefaults
240
254
  // because that restricts it to [0,1]
241
- ax.position = optsX + xpadFrac + thickFrac;
255
+ ax.position = thickFrac + (isVertical ?
256
+ optsX + xpad / gs.w :
257
+ optsY + ypad / gs.h
258
+ );
242
259
 
243
- if(['top', 'bottom'].indexOf(titleSide) !== -1) {
260
+ var topOrBottom = ['top', 'bottom'].indexOf(titleSide) !== -1;
261
+
262
+ if(isVertical && topOrBottom) {
244
263
  ax.title.side = titleSide;
245
- ax.titlex = optsX + xpadFrac;
246
- ax.titley = vFrac + (title.side === 'top' ? lenFrac - ypadFrac : ypadFrac);
264
+ ax.titlex = optsX + xpad / gs.w;
265
+ ax.titley = vFrac + (title.side === 'top' ? lenFrac - ypad / gs.h : ypad / gs.h);
266
+ }
267
+
268
+ if(!isVertical && !topOrBottom) {
269
+ ax.title.side = titleSide;
270
+ ax.titley = optsY + ypad / gs.h;
271
+ ax.titlex = vFrac + xpad / gs.w; // right side
247
272
  }
248
273
 
249
274
  if(line.color && opts.tickmode === 'auto') {
@@ -268,9 +293,12 @@ function drawColorBar(g, opts, gd) {
268
293
 
269
294
  // set domain after init, because we may want to
270
295
  // allow it outside [0,1]
271
- ax.domain = [
272
- vFrac + ypadFrac,
273
- vFrac + lenFrac - ypadFrac
296
+ ax.domain = isVertical ? [
297
+ vFrac + ypad / gs.h,
298
+ vFrac + lenFrac - ypad / gs.h
299
+ ] : [
300
+ vFrac + xpad / gs.w,
301
+ vFrac + lenFrac - xpad / gs.w
274
302
  ];
275
303
 
276
304
  ax.setScale();
@@ -280,9 +308,13 @@ function drawColorBar(g, opts, gd) {
280
308
  var titleCont = g.select('.' + cn.cbtitleunshift)
281
309
  .attr('transform', strTranslate(-Math.round(gs.l), -Math.round(gs.t)));
282
310
 
311
+ var ticklabelposition = ax.ticklabelposition;
312
+ var titleFontSize = ax.title.font.size;
313
+
283
314
  var axLayer = g.select('.' + cn.cbaxis);
284
315
  var titleEl;
285
316
  var titleHeight = 0;
317
+ var titleWidth = 0;
286
318
 
287
319
  function drawTitle(titleClass, titleOpts) {
288
320
  var dfltTitleOpts = {
@@ -307,54 +339,98 @@ function drawColorBar(g, opts, gd) {
307
339
  }
308
340
 
309
341
  function drawDummyTitle() {
310
- if(['top', 'bottom'].indexOf(titleSide) !== -1) {
311
- // draw the title so we know how much room it needs
312
- // when we squish the axis. This one only applies to
313
- // top or bottom titles, not right side.
314
- var x = gs.l + (optsX + xpadFrac) * gs.w;
315
- var fontSize = ax.title.font.size;
316
- var y;
342
+ // draw the title so we know how much room it needs
343
+ // when we squish the axis.
344
+ // On vertical colorbars this only applies to top or bottom titles, not right side.
345
+ // On horizontal colorbars this only applies to right, etc.
346
+
347
+ if(
348
+ (isVertical && topOrBottom) ||
349
+ (!isVertical && !topOrBottom)
350
+ ) {
351
+ var x, y;
317
352
 
318
353
  if(titleSide === 'top') {
319
- y = (1 - (vFrac + lenFrac - ypadFrac)) * gs.h +
320
- gs.t + 3 + fontSize * 0.75;
321
- } else {
322
- y = (1 - (vFrac + ypadFrac)) * gs.h +
323
- gs.t - 3 - fontSize * 0.25;
354
+ x = xpad + gs.l + gs.w * optsX;
355
+ y = ypad + gs.t + gs.h * (1 - vFrac - lenFrac) + 3 + titleFontSize * 0.75;
356
+ }
357
+
358
+ if(titleSide === 'bottom') {
359
+ x = xpad + gs.l + gs.w * optsX;
360
+ y = ypad + gs.t + gs.h * (1 - vFrac) - 3 - titleFontSize * 0.25;
361
+ }
362
+
363
+ if(titleSide === 'right') {
364
+ y = ypad + gs.t + gs.h * optsY + 3 + titleFontSize * 0.75;
365
+ x = xpad + gs.l + gs.w * vFrac;
324
366
  }
367
+
325
368
  drawTitle(ax._id + 'title', {
326
- attributes: {x: x, y: y, 'text-anchor': 'start'}
369
+ attributes: {x: x, y: y, 'text-anchor': isVertical ? 'start' : 'middle'}
327
370
  });
328
371
  }
329
372
  }
330
373
 
331
374
  function drawCbTitle() {
332
- if(['top', 'bottom'].indexOf(titleSide) === -1) {
333
- var fontSize = ax.title.font.size;
334
- var y = ax._offset + ax._length / 2;
335
- var x = gs.l + (ax.position || 0) * gs.w + ((ax.side === 'right') ?
336
- 10 + fontSize * ((ax.showticklabels ? 1 : 0.5)) :
337
- -10 - fontSize * ((ax.showticklabels ? 0.5 : 0)));
338
-
339
- // the 'h' + is a hack to get around the fact that
340
- // convertToTspans rotates any 'y...' class by 90 degrees.
341
- // TODO: find a better way to control this.
342
- drawTitle('h' + ax._id + 'title', {
375
+ if(
376
+ (isVertical && !topOrBottom) ||
377
+ (!isVertical && topOrBottom)
378
+ ) {
379
+ var pos = ax.position || 0;
380
+ var mid = ax._offset + ax._length / 2;
381
+ var x, y;
382
+
383
+ if(titleSide === 'right') {
384
+ y = mid;
385
+ x = gs.l + gs.w * pos + 10 + titleFontSize * (
386
+ ax.showticklabels ? 1 : 0.5
387
+ );
388
+ } else {
389
+ x = mid;
390
+
391
+ if(titleSide === 'bottom') {
392
+ y = gs.t + gs.h * pos + 10 + (
393
+ ticklabelposition.indexOf('inside') === -1 ?
394
+ ax.tickfont.size :
395
+ 0
396
+ ) + (
397
+ ax.ticks !== 'intside' ?
398
+ opts.ticklen || 0 :
399
+ 0
400
+ );
401
+ }
402
+
403
+ if(titleSide === 'top') {
404
+ var nlines = title.text.split('<br>').length;
405
+ y = gs.t + gs.h * pos + 10 - thickPx - LINE_SPACING * titleFontSize * nlines;
406
+ }
407
+ }
408
+
409
+ drawTitle((isVertical ?
410
+ // the 'h' + is a hack to get around the fact that
411
+ // convertToTspans rotates any 'y...' class by 90 degrees.
412
+ // TODO: find a better way to control this.
413
+ 'h' :
414
+ 'v'
415
+ ) + ax._id + 'title', {
343
416
  avoid: {
344
417
  selection: d3.select(gd).selectAll('g.' + ax._id + 'tick'),
345
418
  side: titleSide,
346
- offsetLeft: gs.l,
347
- offsetTop: 0,
348
- maxShift: fullLayout.width
419
+ offsetTop: isVertical ? 0 : gs.t,
420
+ offsetLeft: isVertical ? gs.l : 0,
421
+ maxShift: isVertical ? fullLayout.width : fullLayout.height
349
422
  },
350
423
  attributes: {x: x, y: y, 'text-anchor': 'middle'},
351
- transform: {rotate: '-90', offset: 0}
424
+ transform: {rotate: isVertical ? -90 : 0, offset: 0}
352
425
  });
353
426
  }
354
427
  }
355
428
 
356
429
  function drawAxis() {
357
- if(['top', 'bottom'].indexOf(titleSide) !== -1) {
430
+ if(
431
+ (!isVertical && !topOrBottom) ||
432
+ (isVertical && topOrBottom)
433
+ ) {
358
434
  // squish the axis top to make room for the title
359
435
  var titleGroup = g.select('.' + cn.cbtitle);
360
436
  var titleText = titleGroup.select('text');
@@ -366,39 +442,63 @@ function drawColorBar(g, opts, gd) {
366
442
  if(titleText.node()) {
367
443
  lineSize = parseInt(titleText.node().style.fontSize, 10) * LINE_SPACING;
368
444
  }
445
+
446
+ var bb;
369
447
  if(mathJaxNode) {
370
- titleHeight = Drawing.bBox(mathJaxNode).height;
448
+ bb = Drawing.bBox(mathJaxNode);
449
+ titleWidth = bb.width;
450
+ titleHeight = bb.height;
371
451
  if(titleHeight > lineSize) {
372
452
  // not entirely sure how mathjax is doing
373
453
  // vertical alignment, but this seems to work.
374
454
  titleTrans[1] -= (titleHeight - lineSize) / 2;
375
455
  }
376
456
  } else if(titleText.node() && !titleText.classed(cn.jsPlaceholder)) {
377
- titleHeight = Drawing.bBox(titleText.node()).height;
457
+ bb = Drawing.bBox(titleText.node());
458
+ titleWidth = bb.width;
459
+ titleHeight = bb.height;
378
460
  }
379
- if(titleHeight) {
380
- // buffer btwn colorbar and title
381
- // TODO: configurable
382
- titleHeight += 5;
383
461
 
384
- if(titleSide === 'top') {
385
- ax.domain[1] -= titleHeight / gs.h;
386
- titleTrans[1] *= -1;
387
- } else {
388
- ax.domain[0] += titleHeight / gs.h;
389
- var nlines = svgTextUtils.lineCount(titleText);
390
- titleTrans[1] += (1 - nlines) * lineSize;
462
+ if(isVertical) {
463
+ if(titleHeight) {
464
+ // buffer btwn colorbar and title
465
+ // TODO: configurable
466
+ titleHeight += 5;
467
+
468
+ if(titleSide === 'top') {
469
+ ax.domain[1] -= titleHeight / gs.h;
470
+ titleTrans[1] *= -1;
471
+ } else {
472
+ ax.domain[0] += titleHeight / gs.h;
473
+ var nlines = svgTextUtils.lineCount(titleText);
474
+ titleTrans[1] += (1 - nlines) * lineSize;
475
+ }
476
+
477
+ titleGroup.attr('transform', strTranslate(titleTrans[0], titleTrans[1]));
478
+ ax.setScale();
479
+ }
480
+ } else { // horizontal colorbars
481
+ if(titleWidth) {
482
+ if(titleSide === 'right') {
483
+ ax.domain[0] += (titleWidth + titleFontSize / 2) / gs.w;
484
+ }
485
+
486
+ titleGroup.attr('transform', strTranslate(titleTrans[0], titleTrans[1]));
487
+ ax.setScale();
391
488
  }
392
-
393
- titleGroup.attr('transform', strTranslate(titleTrans[0], titleTrans[1]));
394
- ax.setScale();
395
489
  }
396
490
  }
397
491
 
398
492
  g.selectAll('.' + cn.cbfills + ',.' + cn.cblines)
399
- .attr('transform', strTranslate(0, Math.round(gs.h * (1 - ax.domain[1]))));
493
+ .attr('transform', isVertical ?
494
+ strTranslate(0, Math.round(gs.h * (1 - ax.domain[1]))) :
495
+ strTranslate(Math.round(gs.w * ax.domain[0]), 0)
496
+ );
400
497
 
401
- axLayer.attr('transform', strTranslate(0, Math.round(-gs.t)));
498
+ axLayer.attr('transform', isVertical ?
499
+ strTranslate(0, Math.round(-gs.t)) :
500
+ strTranslate(Math.round(-gs.l), 0)
501
+ );
402
502
 
403
503
  var fills = g.select('.' + cn.cbfills)
404
504
  .selectAll('rect.' + cn.cbfill)
@@ -424,20 +524,22 @@ function drawColorBar(g, opts, gd) {
424
524
 
425
525
  // offset the side adjoining the next rectangle so they
426
526
  // overlap, to prevent antialiasing gaps
427
- z[1] = Lib.constrain(z[1] + (z[1] > z[0]) ? 1 : -1, zBounds[0], zBounds[1]);
428
-
527
+ if(isVertical) {
528
+ z[1] = Lib.constrain(z[1] + (z[1] > z[0]) ? 1 : -1, zBounds[0], zBounds[1]);
529
+ } /* else {
530
+ // TODO: horizontal case
531
+ } */
429
532
 
430
533
  // Colorbar cannot currently support opacities so we
431
534
  // use an opaque fill even when alpha channels present
432
- var fillEl = d3.select(this).attr({
433
- x: uPx,
434
- width: Math.max(thickPx, 2),
435
- y: d3.min(z),
436
- height: Math.max(d3.max(z) - d3.min(z), 2),
437
- });
535
+ var fillEl = d3.select(this)
536
+ .attr(isVertical ? 'x' : 'y', uPx)
537
+ .attr(isVertical ? 'y' : 'x', d3.min(z))
538
+ .attr(isVertical ? 'width' : 'height', Math.max(thickPx, 2))
539
+ .attr(isVertical ? 'height' : 'width', Math.max(d3.max(z) - d3.min(z), 2));
438
540
 
439
541
  if(opts._fillgradient) {
440
- Drawing.gradient(fillEl, gd, opts._id, 'vertical', opts._fillgradient, 'fill');
542
+ Drawing.gradient(fillEl, gd, opts._id, isVertical ? 'vertical' : 'horizontalreversed', opts._fillgradient, 'fill');
441
543
  } else {
442
544
  // tinycolor can't handle exponents and
443
545
  // at this scale, removing it makes no difference.
@@ -453,9 +555,15 @@ function drawColorBar(g, opts, gd) {
453
555
  .classed(cn.cbline, true);
454
556
  lines.exit().remove();
455
557
  lines.each(function(d) {
558
+ var a = uPx;
559
+ var b = (Math.round(ax.c2p(d)) + (line.width / 2) % 1);
560
+
456
561
  d3.select(this)
457
- .attr('d', 'M' + uPx + ',' +
458
- (Math.round(ax.c2p(d)) + (line.width / 2) % 1) + 'h' + thickPx)
562
+ .attr('d', 'M' +
563
+ (isVertical ? a + ',' + b : b + ',' + a) +
564
+ (isVertical ? 'h' : 'v') +
565
+ thickPx
566
+ )
459
567
  .call(Drawing.lineGroupStyle, line.width, lineColormap(d), line.dash);
460
568
  });
461
569
 
@@ -488,82 +596,211 @@ function drawColorBar(g, opts, gd) {
488
596
  // TODO: why are we redrawing multiple times now with this?
489
597
  // I guess autoMargin doesn't like being post-promise?
490
598
  function positionCB() {
599
+ var bb;
491
600
  var innerThickness = thickPx + outlinewidth / 2;
492
- if(ax.ticklabelposition.indexOf('inside') === -1) {
493
- innerThickness += Drawing.bBox(axLayer.node()).width;
601
+ if(ticklabelposition.indexOf('inside') === -1) {
602
+ bb = Drawing.bBox(axLayer.node());
603
+ innerThickness += isVertical ? bb.width : bb.height;
494
604
  }
495
605
 
496
606
  titleEl = titleCont.select('text');
497
607
 
608
+ var titleWidth = 0;
609
+
610
+ var topSideVertical = isVertical && titleSide === 'top';
611
+ var rightSideHorizontal = !isVertical && titleSide === 'right';
612
+
613
+ var moveY = 0;
614
+
498
615
  if(titleEl.node() && !titleEl.classed(cn.jsPlaceholder)) {
616
+ var _titleHeight;
617
+
499
618
  var mathJaxNode = titleCont.select('.h' + ax._id + 'title-math-group').node();
500
- var titleWidth;
501
- if(mathJaxNode && ['top', 'bottom'].indexOf(titleSide) !== -1) {
502
- titleWidth = Drawing.bBox(mathJaxNode).width;
619
+ if(mathJaxNode && (
620
+ (isVertical && topOrBottom) ||
621
+ (!isVertical && !topOrBottom)
622
+ )) {
623
+ bb = Drawing.bBox(mathJaxNode);
624
+ titleWidth = bb.width;
625
+ _titleHeight = bb.height;
503
626
  } else {
504
627
  // note: the formula below works for all title sides,
505
628
  // (except for top/bottom mathjax, above)
506
629
  // but the weird gs.l is because the titleunshift
507
630
  // transform gets removed by Drawing.bBox
508
- titleWidth = Drawing.bBox(titleCont.node()).right - uPx - gs.l;
631
+ bb = Drawing.bBox(titleCont.node());
632
+ titleWidth = bb.right - gs.l - (isVertical ? uPx : vPx);
633
+ _titleHeight = bb.bottom - gs.t - (isVertical ? vPx : uPx);
634
+
635
+ if(
636
+ !isVertical && titleSide === 'top'
637
+ ) {
638
+ innerThickness += bb.height;
639
+ moveY = bb.height;
640
+ }
641
+ }
642
+
643
+ if(rightSideHorizontal) {
644
+ titleEl.attr('transform', strTranslate(titleWidth / 2 + titleFontSize / 2, 0));
645
+
646
+ titleWidth *= 2;
509
647
  }
510
- innerThickness = Math.max(innerThickness, titleWidth);
648
+
649
+ innerThickness = Math.max(innerThickness,
650
+ isVertical ? titleWidth : _titleHeight
651
+ );
511
652
  }
512
653
 
513
- var outerThickness = 2 * xpad + innerThickness + borderwidth + outlinewidth / 2;
654
+ var outerThickness = (isVertical ?
655
+ xpad :
656
+ ypad
657
+ ) * 2 + innerThickness + borderwidth + outlinewidth / 2;
658
+
659
+ var hColorbarMoveTitle = 0;
660
+ if(!isVertical && title.text && yanchor === 'bottom' && optsY <= 0) {
661
+ hColorbarMoveTitle = outerThickness / 2;
662
+
663
+ outerThickness += hColorbarMoveTitle;
664
+ moveY += hColorbarMoveTitle;
665
+ }
666
+ fullLayout._hColorbarMoveTitle = hColorbarMoveTitle;
667
+ fullLayout._hColorbarMoveCBTitle = moveY;
514
668
 
515
- g.select('.' + cn.cbbg).attr({
516
- x: uPx - xpad - (borderwidth + outlinewidth) / 2,
517
- y: vPx - lenPx - yExtraPx,
518
- width: Math.max(outerThickness, 2),
519
- height: Math.max(lenPx + 2 * yExtraPx, 2)
520
- })
521
- .call(Color.fill, opts.bgcolor)
669
+ var extraW = borderwidth + outlinewidth;
670
+
671
+ g.select('.' + cn.cbbg)
672
+ .attr('x', (isVertical ? uPx : vPx) - extraW / 2 - (isVertical ? xpad : 0))
673
+ .attr('y', (isVertical ? vPx : uPx) - (isVertical ? lenPx : ypad + moveY - hColorbarMoveTitle))
674
+ .attr(isVertical ? 'width' : 'height', Math.max(outerThickness - hColorbarMoveTitle, 2))
675
+ .attr(isVertical ? 'height' : 'width', Math.max(lenPx + extraW, 2))
676
+ .call(Color.fill, bgcolor)
522
677
  .call(Color.stroke, opts.bordercolor)
523
678
  .style('stroke-width', borderwidth);
524
679
 
525
- g.selectAll('.' + cn.cboutline).attr({
526
- x: uPx,
527
- y: vPx - lenPx + ypad + (titleSide === 'top' ? titleHeight : 0),
528
- width: Math.max(thickPx, 2),
529
- height: Math.max(lenPx - 2 * ypad - titleHeight, 2)
530
- })
680
+ var moveX = rightSideHorizontal ? Math.max(titleWidth - 10, 0) : 0;
681
+
682
+ g.selectAll('.' + cn.cboutline)
683
+ .attr('x', (isVertical ? uPx : vPx + xpad) + moveX)
684
+ .attr('y', (isVertical ? vPx + ypad - lenPx : uPx) + (topSideVertical ? titleHeight : 0))
685
+ .attr(isVertical ? 'width' : 'height', Math.max(thickPx, 2))
686
+ .attr(isVertical ? 'height' : 'width', Math.max(lenPx - (isVertical ?
687
+ 2 * ypad + titleHeight :
688
+ 2 * xpad + moveX
689
+ ), 2))
531
690
  .call(Color.stroke, opts.outlinecolor)
532
691
  .style({
533
692
  fill: 'none',
534
693
  'stroke-width': outlinewidth
535
694
  });
536
695
 
537
- // fix positioning for xanchor!='left'
538
- var xoffset = ({center: 0.5, right: 1}[xanchor] || 0) * outerThickness;
539
- g.attr('transform', strTranslate(gs.l - xoffset, gs.t));
696
+ g.attr('transform', strTranslate(
697
+ gs.l - (isVertical ? xRatio * outerThickness : 0),
698
+ gs.t - (isVertical ? 0 : (1 - yRatio) * outerThickness - moveY)
699
+ ));
700
+
701
+ if(!isVertical && (
702
+ borderwidth || (
703
+ tinycolor(bgcolor).getAlpha() &&
704
+ !tinycolor.equals(fullLayout.paper_bgcolor, bgcolor)
705
+ )
706
+ )) {
707
+ // for horizontal colorbars when there is a border line or having different background color
708
+ // hide/adjust x positioning for the first/last tick labels if they go outside the border
709
+ var tickLabels = axLayer.selectAll('text');
710
+ var numTicks = tickLabels[0].length;
711
+
712
+ var border = g.select('.' + cn.cbbg).node();
713
+ var oBb = Drawing.bBox(border);
714
+ var oTr = Drawing.getTranslate(g);
715
+
716
+ var TEXTPAD = 2;
717
+
718
+ tickLabels.each(function(d, i) {
719
+ var first = 0;
720
+ var last = numTicks - 1;
721
+ if(i === first || i === last) {
722
+ var iBb = Drawing.bBox(this);
723
+ var iTr = Drawing.getTranslate(this);
724
+ var deltaX;
725
+
726
+ if(i === last) {
727
+ var iRight = iBb.right + iTr.x;
728
+ var oRight = oBb.right + oTr.x + vPx - borderwidth - TEXTPAD + optsX;
729
+
730
+ deltaX = oRight - iRight;
731
+ if(deltaX > 0) deltaX = 0;
732
+ } else if(i === first) {
733
+ var iLeft = iBb.left + iTr.x;
734
+ var oLeft = oBb.left + oTr.x + vPx + borderwidth + TEXTPAD;
735
+
736
+ deltaX = oLeft - iLeft;
737
+ if(deltaX < 0) deltaX = 0;
738
+ }
739
+
740
+ if(deltaX) {
741
+ if(numTicks < 3) { // adjust position
742
+ this.setAttribute('transform',
743
+ 'translate(' + deltaX + ',0) ' +
744
+ this.getAttribute('transform')
745
+ );
746
+ } else { // hide
747
+ this.setAttribute('visibility', 'hidden');
748
+ }
749
+ }
750
+ }
751
+ });
752
+ }
540
753
 
541
754
  // auto margin adjustment
542
755
  var marginOpts = {};
756
+ var lFrac = FROM_TL[xanchor];
757
+ var rFrac = FROM_BR[xanchor];
543
758
  var tFrac = FROM_TL[yanchor];
544
759
  var bFrac = FROM_BR[yanchor];
545
- if(lenmode === 'pixels') {
546
- marginOpts.y = optsY;
547
- marginOpts.t = lenPx * tFrac;
548
- marginOpts.b = lenPx * bFrac;
549
- } else {
550
- marginOpts.t = marginOpts.b = 0;
551
- marginOpts.yt = optsY + len * tFrac;
552
- marginOpts.yb = optsY - len * bFrac;
553
- }
554
760
 
555
- var lFrac = FROM_TL[xanchor];
556
- var rFrac = FROM_BR[xanchor];
557
- if(thicknessmode === 'pixels') {
558
- marginOpts.x = optsX;
559
- marginOpts.l = outerThickness * lFrac;
560
- marginOpts.r = outerThickness * rFrac;
561
- } else {
562
- var extraThickness = outerThickness - thickPx;
563
- marginOpts.l = extraThickness * lFrac;
564
- marginOpts.r = extraThickness * rFrac;
565
- marginOpts.xl = optsX - thickness * lFrac;
566
- marginOpts.xr = optsX + thickness * rFrac;
761
+ var extraThickness = outerThickness - thickPx;
762
+ if(isVertical) {
763
+ if(lenmode === 'pixels') {
764
+ marginOpts.y = optsY;
765
+ marginOpts.t = lenPx * tFrac;
766
+ marginOpts.b = lenPx * bFrac;
767
+ } else {
768
+ marginOpts.t = marginOpts.b = 0;
769
+ marginOpts.yt = optsY + len * tFrac;
770
+ marginOpts.yb = optsY - len * bFrac;
771
+ }
772
+
773
+ if(thicknessmode === 'pixels') {
774
+ marginOpts.x = optsX;
775
+ marginOpts.l = outerThickness * lFrac;
776
+ marginOpts.r = outerThickness * rFrac;
777
+ } else {
778
+ marginOpts.l = extraThickness * lFrac;
779
+ marginOpts.r = extraThickness * rFrac;
780
+ marginOpts.xl = optsX - thickness * lFrac;
781
+ marginOpts.xr = optsX + thickness * rFrac;
782
+ }
783
+ } else { // horizontal colorbars
784
+ if(lenmode === 'pixels') {
785
+ marginOpts.x = optsX;
786
+ marginOpts.l = lenPx * lFrac;
787
+ marginOpts.r = lenPx * rFrac;
788
+ } else {
789
+ marginOpts.l = marginOpts.r = 0;
790
+ marginOpts.xl = optsX + len * lFrac;
791
+ marginOpts.xr = optsX - len * rFrac;
792
+ }
793
+
794
+ if(thicknessmode === 'pixels') {
795
+ marginOpts.y = 1 - optsY;
796
+ marginOpts.t = outerThickness * tFrac;
797
+ marginOpts.b = outerThickness * bFrac;
798
+ } else {
799
+ marginOpts.t = extraThickness * tFrac;
800
+ marginOpts.b = extraThickness * bFrac;
801
+ marginOpts.yt = optsY - thickness * tFrac;
802
+ marginOpts.yb = optsY + thickness * bFrac;
803
+ }
567
804
  }
568
805
 
569
806
  Plots.autoMargin(gd, opts._id, marginOpts);
@@ -580,6 +817,7 @@ function drawColorBar(g, opts, gd) {
580
817
  }
581
818
 
582
819
  function makeEditable(g, opts, gd) {
820
+ var isVertical = opts.orientation === 'v';
583
821
  var fullLayout = gd._fullLayout;
584
822
  var gs = fullLayout._size;
585
823
  var t0, xf, yf;
@@ -594,9 +832,13 @@ function makeEditable(g, opts, gd) {
594
832
  moveFn: function(dx, dy) {
595
833
  g.attr('transform', t0 + strTranslate(dx, dy));
596
834
 
597
- xf = dragElement.align(opts._uFrac + (dx / gs.w), opts._thickFrac,
835
+ xf = dragElement.align(
836
+ (isVertical ? opts._uFrac : opts._vFrac) + (dx / gs.w),
837
+ isVertical ? opts._thickFrac : opts._lenFrac,
598
838
  0, 1, opts.xanchor);
599
- yf = dragElement.align(opts._vFrac - (dy / gs.h), opts._lenFrac,
839
+ yf = dragElement.align(
840
+ (isVertical ? opts._vFrac : (1 - opts._uFrac)) - (dy / gs.h),
841
+ isVertical ? opts._lenFrac : opts._thickFrac,
600
842
  0, 1, opts.yanchor);
601
843
 
602
844
  var csr = dragElement.getCursor(xf, yf, opts.xanchor, opts.yanchor);
@@ -673,6 +915,8 @@ function calcLevels(gd, opts, zrange) {
673
915
  function mockColorBarAxis(gd, opts, zrange) {
674
916
  var fullLayout = gd._fullLayout;
675
917
 
918
+ var isVertical = opts.orientation === 'v';
919
+
676
920
  var cbAxisIn = {
677
921
  type: 'linear',
678
922
  range: zrange,
@@ -703,17 +947,19 @@ function mockColorBarAxis(gd, opts, zrange) {
703
947
  title: opts.title,
704
948
  showline: true,
705
949
  anchor: 'free',
706
- side: 'right',
950
+ side: isVertical ? 'right' : 'bottom',
707
951
  position: 1
708
952
  };
709
953
 
954
+ var letter = isVertical ? 'y' : 'x';
955
+
710
956
  var cbAxisOut = {
711
957
  type: 'linear',
712
- _id: 'y' + opts._id
958
+ _id: letter + opts._id
713
959
  };
714
960
 
715
961
  var axisOptions = {
716
- letter: 'y',
962
+ letter: letter,
717
963
  font: fullLayout.font,
718
964
  noHover: true,
719
965
  noTickson: true,