Skip to content

Commit ed36d56

Browse files
committed
feat(plugin.legend): make navigation object undefined unless enabled
The navigation object was being populated even with navigation disabled. Now, the navigation object is only initialized if the feature is enabled. Some improvements have also been made to code readability.
1 parent 8a2fa75 commit ed36d56

File tree

2 files changed

+258
-137
lines changed

2 files changed

+258
-137
lines changed

src/plugins/plugin.legend.js

Lines changed: 120 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -57,26 +57,6 @@ export class Legend extends Element {
5757
*/
5858
this._hoveredItem = null;
5959

60-
this.navigation = {
61-
active: false,
62-
page: 0,
63-
totalPages: 0,
64-
itemWidth: 0,
65-
itemHeight: 0,
66-
navWidth: 0,
67-
navHeight: 0,
68-
maxBlocks: 0,
69-
blocks: undefined,
70-
text: undefined,
71-
prev: undefined,
72-
next: undefined,
73-
legendItems: undefined,
74-
_width: 0,
75-
_height: 0,
76-
_maxWidth: 0,
77-
_maxHeight: 0,
78-
};
79-
8060
// Are we in doughnut mode which has a different data type
8161
this.doughnutMode = false;
8262

@@ -98,6 +78,7 @@ export class Legend extends Element {
9878
this.position = undefined;
9979
this.weight = undefined;
10080
this.fullSize = undefined;
81+
this.navigation = undefined;
10182
}
10283

10384
update(maxWidth, maxHeight, margins) {
@@ -139,17 +120,16 @@ export class Legend extends Element {
139120
legendItems.reverse();
140121
}
141122

142-
this.navigation.legendItems = this.legendItems = legendItems;
123+
this.legendItems = legendItems;
143124
this._computeNavigation();
144125
}
145126

146127
/**
147128
* @private
148129
*/
149-
_resetNavigation() {
150-
if (this.navigation.active) {
151-
Object.assign(this.navigation, {
152-
active: false,
130+
_initNavigation(override) {
131+
if (!this.navigation) {
132+
this.navigation = {
153133
page: 0,
154134
totalPages: 0,
155135
itemWidth: 0,
@@ -166,7 +146,11 @@ export class Legend extends Element {
166146
_height: 0,
167147
_maxWidth: 0,
168148
_maxHeight: 0,
169-
});
149+
};
150+
}
151+
152+
if (override) {
153+
Object.assign(this.navigation, override);
170154
}
171155
}
172156

@@ -178,25 +162,28 @@ export class Legend extends Element {
178162
const {navigation: navOpts, labels: labelOpts} = options;
179163

180164
if (!(navOpts && navOpts.display)) {
181-
this._resetNavigation();
165+
this.navigation = undefined;
182166
return;
183167
}
184168
const isHorizontal = this.isHorizontal();
185169

186-
this.navigation.active = true;
187-
this.navigation.totalPages = 0;
188-
this.navigation._width = this.navigation._height = 0;
189-
this.navigation._maxWidth = this.navigation._maxHeight = 0;
190-
this.navigation.itemWidth = this.navigation.itemHeight = 0;
191-
this.navigation.maxBlocks = 0;
170+
this._initNavigation({
171+
totalPages: 0,
172+
_width: 0,
173+
_height: 0,
174+
_maxWidth: 0,
175+
_maxHeight: 0,
176+
itemWidth: 0,
177+
itemHeight: 0,
178+
maxBlocks: 0,
179+
});
192180

193-
const {arrowSize} = navOpts;
194181
const labelFont = toFont(labelOpts.font);
195182
const {boxWidth, itemHeight: _itemHeight} = getBoxSize(labelOpts, labelFont.size);
196183
const font = toFont(navOpts.font);
197184

198185
const padding = toPadding(navOpts.padding);
199-
this.navigation.navHeight = Math.max(font.size, arrowSize) + padding.height;
186+
this.navigation.navHeight = Math.max(font.size, navOpts.arrowSize) + padding.height;
200187

201188
const grid = getGridAxis(navOpts.grid);
202189

@@ -325,17 +312,16 @@ export class Legend extends Element {
325312
}
326313

327314
buildNavigation() {
328-
const {ctx, options: {align, navigation: navOpts, labels: labelOpts}} = this;
329-
const {active, totalPages, navHeight} = this.navigation;
330-
331-
if (!active) {
315+
if (!this.navigation) {
332316
return;
333317
}
318+
const {ctx, options: {align, navigation: navOpts, labels: labelOpts}} = this;
319+
const {totalPages, navHeight} = this.navigation;
334320

335321
const font = toFont(navOpts.font);
336322

337323
if (totalPages < 1 || (totalPages === 1 && navOpts.display === 'auto')) {
338-
this._resetNavigation();
324+
this.navigation = undefined;
339325
return;
340326
}
341327

@@ -394,7 +380,12 @@ export class Legend extends Element {
394380
*/
395381
_getLegendItemWidth(legendItem, boxWidth, labelFont) {
396382
const hitboxWidth = calculateItemWidth(legendItem, boxWidth, labelFont, this.ctx);
397-
const itemWidth = this.navigation.itemWidth || hitboxWidth;
383+
let itemWidth = hitboxWidth;
384+
385+
if (this.navigation && this.navigation.itemWidth) {
386+
itemWidth = this.navigation.itemWidth;
387+
}
388+
398389
return {itemWidth, hitboxWidth};
399390
}
400391

@@ -403,10 +394,25 @@ export class Legend extends Element {
403394
*/
404395
_getLegendItemHeight(legendItem, _itemHeight, labelFont) {
405396
const hitboxHeight = calculateItemHeight(_itemHeight, legendItem, labelFont.lineHeight);
406-
const itemHeight = this.navigation.itemHeight || hitboxHeight;
397+
let itemHeight = hitboxHeight;
398+
399+
if (this.navigation && this.navigation.itemHeight) {
400+
itemHeight = this.navigation.itemHeight;
401+
}
402+
407403
return {itemHeight, hitboxHeight};
408404
}
409405

406+
/**
407+
* @private
408+
*/
409+
_getVisibleLegendItems() {
410+
if (this.navigation) {
411+
return this.navigation.legendItems;
412+
}
413+
return this.legendItems;
414+
}
415+
410416
fit() {
411417
const {options, ctx} = this;
412418

@@ -433,15 +439,23 @@ export class Legend extends Element {
433439
if (isHorizontal) {
434440
width = this.maxWidth; // fill all the width
435441
height = this._fitRows(titleHeight, labelFont, boxWidth, itemHeight) + 10;
442+
443+
if (this.navigation) {
444+
height = this.navigation._height;
445+
}
436446
} else {
437447
height = this.maxHeight; // fill all the height
438448
width = this._fitCols(titleHeight, labelFont, boxWidth, itemHeight) + 10;
449+
450+
if (this.navigation) {
451+
width = this.navigation._width;
452+
}
439453
}
440454

441455
const maxWidth = isHorizontal ? this.maxWidth : (options.maxWidth || this.maxWidth);
442456
const maxHeight = isHorizontal ? (options.maxHeight || this.maxHeight) : this.maxHeight;
443-
this.width = Math.min(this.navigation._width || width, maxWidth);
444-
this.height = Math.min(this.navigation._height || height, maxHeight);
457+
this.width = Math.min(width, maxWidth);
458+
this.height = Math.min(height, maxHeight);
445459
}
446460

447461
/**
@@ -460,7 +474,8 @@ export class Legend extends Element {
460474
let row = 0;
461475
let top = 0;
462476
let currentLineHeight = 0;
463-
this.navigation.legendItems.forEach((legendItem, i) => {
477+
478+
this._getVisibleLegendItems().forEach((legendItem, i) => {
464479
const {itemWidth, itemHeight, hitboxWidth, hitboxHeight} = this._getLegendItemSize(legendItem, boxWidth, _itemHeight, labelFont);
465480

466481
if (i > 0 && lineWidths[lineWidths.length - 1] + itemWidth + 2 * padding > maxWidth) {
@@ -487,7 +502,11 @@ export class Legend extends Element {
487502
const {maxHeight, options: {labels: {padding}}} = this;
488503
const hitboxes = this.legendHitBoxes = [];
489504
const columnSizes = this.columnSizes = [];
490-
const heightLimit = maxHeight - titleHeight - this.navigation.navHeight;
505+
let heightLimit = maxHeight - titleHeight;
506+
507+
if (this.navigation) {
508+
heightLimit -= this.navigation.navHeight;
509+
}
491510

492511
let totalWidth = padding;
493512
let currentColWidth = 0;
@@ -496,7 +515,7 @@ export class Legend extends Element {
496515
let left = 0;
497516
let col = 0;
498517

499-
this.navigation.legendItems.forEach((legendItem, i) => {
518+
this._getVisibleLegendItems().forEach((legendItem, i) => {
500519
const {itemWidth, itemHeight, hitboxWidth, hitboxHeight} = this._getLegendItemSize(legendItem, boxWidth, _itemHeight, labelFont);
501520

502521
// If too tall, go to new column
@@ -545,7 +564,11 @@ export class Legend extends Element {
545564
left += hitbox.offsetWidth + padding;
546565
}
547566
} else {
548-
const bottom = this.bottom - this.navigation.navHeight;
567+
let bottom = this.bottom;
568+
569+
if (this.navigation) {
570+
bottom -= this.navigation.navHeight;
571+
}
549572

550573
let col = 0;
551574
let top = _alignStartEnd(align, this.top + titleHeight + padding, bottom - this.columnSizes[col].height);
@@ -674,7 +697,11 @@ export class Legend extends Element {
674697
// Horizontal
675698
const isHorizontal = this.isHorizontal();
676699
const titleHeight = this._computeTitleHeight();
677-
const bottom = this.bottom - this.navigation.navHeight;
700+
let bottom = this.bottom;
701+
702+
if (this.navigation) {
703+
bottom -= this.navigation.navHeight;
704+
}
678705

679706
if (isHorizontal) {
680707
cursor = {
@@ -693,7 +720,7 @@ export class Legend extends Element {
693720
overrideTextDirection(this.ctx, opts.textDirection);
694721

695722
let currentLineHeight = 0;
696-
this.navigation.legendItems.forEach((legendItem, i) => {
723+
this._getVisibleLegendItems().forEach((legendItem, i) => {
697724
ctx.strokeStyle = legendItem.fontColor; // for strikethrough effect
698725
ctx.fillStyle = legendItem.fontColor; // render in correct colour
699726

@@ -745,10 +772,13 @@ export class Legend extends Element {
745772
* @private
746773
*/
747774
_drawNavigation() {
775+
if (!this.navigation) {
776+
return;
777+
}
748778
const {ctx, options: {navigation: navOpts}} = this;
749-
const {active, page, totalPages, maxBlocks, prev, next, text} = this.navigation;
779+
const {page, totalPages, maxBlocks, prev, next, text} = this.navigation;
750780

751-
if (!active || (totalPages <= 1 && navOpts.display === 'auto')) {
781+
if (totalPages <= 1 && navOpts.display === 'auto') {
752782
return;
753783
}
754784

@@ -813,13 +843,20 @@ export class Legend extends Element {
813843

814844
if (this.isHorizontal()) {
815845
// Move left / right so that the title is above the legend lines
816-
maxWidth = (this.navigation._maxWidth || Math.max(...this.lineWidths)) + labelOpts.padding;
846+
maxWidth = (this.navigation ? this.navigation._maxWidth : Math.max(...this.lineWidths)) + labelOpts.padding;
817847
y = this.top + topPaddingPlusHalfFontSize;
818848
left = _alignStartEnd(options.align, left, this.right - maxWidth);
819849
} else {
820850
// Move down so that the title is above the legend stack in every alignment
821-
const maxHeight = (this.navigation._maxHeight || this.columnSizes.reduce((acc, size) => Math.max(acc, size.height), 0))
822-
+ labelOpts.padding + this._computeTitleHeight() + this.navigation.navHeight;
851+
let maxHeight;
852+
853+
if (this.navigation) {
854+
maxHeight = this.navigation._maxHeight + this.navigation.navHeight;
855+
} else {
856+
maxHeight = this.columnSizes.reduce((acc, size) => Math.max(acc, size.height), 0);
857+
}
858+
859+
maxHeight += labelOpts.padding + this._computeTitleHeight();
823860
y = topPaddingPlusHalfFontSize + _alignStartEnd(options.align, this.top, this.bottom - maxHeight);
824861
}
825862

@@ -889,11 +926,10 @@ export class Legend extends Element {
889926
* @private
890927
*/
891928
_getNavigationDirAt(x, y) {
892-
const {prev, next} = this.navigation;
893-
894-
if (!(prev && next)) {
929+
if (!(this.navigation && this.navigation.prev && this.navigation.next)) {
895930
return 0;
896931
}
932+
const {prev, next} = this.navigation;
897933

898934
const {arrowSize} = this.options.navigation;
899935
// Add a padding to the clickable area (30% of the arrow size)
@@ -915,19 +951,20 @@ export class Legend extends Element {
915951
* @private
916952
*/
917953
_getLegendItemAt(x, y) {
918-
let i, hitBox, lh;
919-
920954
if (_isBetween(x, this.left, this.right)
921955
&& _isBetween(y, this.top, this.bottom)) {
922956
// See if we are touching one of the dataset boxes
923-
lh = this.legendHitBoxes;
924-
for (i = 0; i < lh.length; ++i) {
957+
const lh = this.legendHitBoxes;
958+
const legendItems = this._getVisibleLegendItems();
959+
let hitBox;
960+
961+
for (let i = 0; i < lh.length; ++i) {
925962
hitBox = lh[i];
926963

927964
if (_isBetween(x, hitBox.left, hitBox.left + hitBox.width)
928965
&& _isBetween(y, hitBox.top, hitBox.top + hitBox.height)) {
929966
// Touching an element
930-
return this.navigation.legendItems[i];
967+
return legendItems[i];
931968
}
932969
}
933970
}
@@ -939,20 +976,25 @@ export class Legend extends Element {
939976
* @private
940977
*/
941978
_handleNavigationEvent(e) {
942-
if (e.type === 'click') {
943-
const dir = this._getNavigationDirAt(e.x, e.y);
944-
if (dir) {
945-
const {page, totalPages} = this.navigation;
946-
const lastPage = totalPages - 1;
947-
const newPage = this.navigation.page = Math.max(0, Math.min(lastPage, this.navigation.page + dir));
948-
949-
if (newPage !== page) {
950-
this.buildNavigation();
951-
this.fit();
952-
this.adjustHitBoxes();
953-
this.chart.render();
954-
}
955-
}
979+
if (!this.navigation || this.navigation.totalPages < 2 || e.type !== 'click') {
980+
return;
981+
}
982+
983+
const dir = this._getNavigationDirAt(e.x, e.y);
984+
985+
if (!dir) {
986+
return;
987+
}
988+
989+
const {page, totalPages} = this.navigation;
990+
const lastPage = totalPages - 1;
991+
const newPage = this.navigation.page = Math.max(0, Math.min(lastPage, this.navigation.page + dir));
992+
993+
if (newPage !== page) {
994+
this.buildNavigation();
995+
this.fit();
996+
this.adjustHitBoxes();
997+
this.chart.render();
956998
}
957999
}
9581000

@@ -961,9 +1003,7 @@ export class Legend extends Element {
9611003
* @param {ChartEvent} e - The event to handle
9621004
*/
9631005
handleEvent(e) {
964-
if (this.navigation.totalPages > 1) {
965-
this._handleNavigationEvent(e);
966-
}
1006+
this._handleNavigationEvent(e);
9671007

9681008
const opts = this.options;
9691009
if (!isListened(e.type, opts)) {

0 commit comments

Comments
 (0)