| @@ -75,11 +75,52 @@ | ||
| 75 | 75 | }); |
| 76 | 76 | chart.__gvnHasTime = found; |
| 77 | 77 | return found; |
| 78 | 78 | } |
| 79 | + // Labels of dates that sit too close together on the axis are moved to a lower line (one below the | |
| 80 | + // other) instead of being drawn over each other. Returns the line level (0, 1, 2) per tick index. | |
| 81 | + function staggerLevels(scale, ticks, withTime) { | |
| 82 | + var sig = ticks.length + ':' + scale.min + ':' + scale.max + ':' + scale.width; | |
| 83 | + if (scale.__gvnStagger && scale.__gvnStagger.sig === sig) return scale.__gvnStagger.levels; | |
| 84 | + var span = (scale.max - scale.min) || 1, width = scale.width || 0, levels = [], px = [], i, j, L, ok; | |
| 85 | + var labelW = (withTime ? 16 : 10) * 7 + 12; // approx. pixel width of "YYYY-MM-DD[ HH:MM]" plus a gap | |
| 86 | + for (i = 0; i < ticks.length; i++) { | |
| 87 | + px[i] = (ticks[i].value - scale.min) / span * width; | |
| 88 | + for (L = 0; L < 3; L++) { | |
| 89 | + ok = true; | |
| 90 | + for (j = 0; j < i; j++) if (levels[j] === L && Math.abs(px[i] - px[j]) < labelW) { ok = false; break; } | |
| 91 | + if (ok) break; | |
| 92 | + } | |
| 93 | + levels[i] = ok ? L : -1; // -1: no free line, the label is hidden (the tooltip still shows the date) | |
| 94 | + } | |
| 95 | + scale.__gvnStagger = { sig: sig, levels: levels }; | |
| 96 | + return levels; | |
| 97 | + } | |
| 79 | 98 | // scales.x.ticks.callback – "this" is the scale |
| 80 | - window.gvnSqlChartsTimeTick = function (value) { | |
| 81 | - return fmt(value, hasTime(this && this.chart)); | |
| 99 | + window.gvnSqlChartsTimeTick = function (value, index, ticks) { | |
| 100 | + var withTime = hasTime(this && this.chart); | |
| 101 | + var label = fmt(value, withTime); | |
| 102 | + if (!this || !ticks || !ticks.length || typeof index !== 'number') return label; | |
| 103 | + var level = staggerLevels(this, ticks, withTime)[index] || 0; | |
| 104 | + if (level < 0) return ''; | |
| 105 | + if (!level) return label; | |
| 106 | + var lines = []; | |
| 107 | + for (var k = 0; k < level; k++) lines.push(''); | |
| 108 | + lines.push(label); | |
| 109 | + return lines; | |
| 110 | + }; | |
| 111 | + // scales.x.afterBuildTicks – label only the dates that exist in the data instead of evenly spaced values | |
| 112 | + window.gvnSqlChartsTimeTicks = function (scale) { | |
| 113 | + var seen = {}, xs = []; | |
| 114 | + (scale.chart.data.datasets || []).forEach(function (ds, i) { | |
| 115 | + if (scale.chart.getDatasetMeta(i).hidden) return; | |
| 116 | + (ds.data || []).forEach(function (p) { | |
| 117 | + if (p && typeof p === 'object' && typeof p.x === 'number' && !seen[p.x]) { seen[p.x] = true; xs.push(p.x); } | |
| 118 | + }); | |
| 119 | + }); | |
| 120 | + if (!xs.length) return; | |
| 121 | + xs.sort(function (a, b) { return a - b; }); | |
| 122 | + scale.ticks = xs.map(function (v) { return { value: v }; }); | |
| 82 | 123 | }; |
| 83 | 124 | // plugins.tooltip.callbacks.title |
| 84 | 125 | window.gvnSqlChartsTimeTooltipTitle = function (items) { |
| 85 | 126 | return items && items.length ? fmt(items[0].parsed.x, hasTime(items[0].chart)) : ''; |