Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix for #3293 #4266

Closed
wants to merge 1 commit into from
Closed

Fix for #3293 #4266

wants to merge 1 commit into from

Conversation

bokysan
Copy link

@bokysan bokysan commented May 17, 2017

Hello,

please find below an implementation of feature #3293.

You are now able to do the following:

options: {
    elements: {
        rectangle: {
            borderSkipped: [ 'left', 'right' ],
       }
    },
   ...
}

Example of implementation is visible here: https://bokysan.github.io/Chart.js/

Please note that the code also includes the initial "undocumented" support setting the borderWidth in the same manner, as suggested in the last comment:

options: {
    elements: {
        rectangle: {
            borderWidth: [ 0, 2, 4, 6 ],
       }
    },
   ...
}

However not all user cases have been tested -- especially with the semi-transparent colours -- so I would leave it as undocumented for the time being.

Cheers,
B

Copy link
Member

@etimberg etimberg left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Left some comments. @simonbrunel any comments?

// - top-bottom + left-right
// - top + right + bottom + left
return !vm.borderWidth ? [] :
vm.borderWidth instanceof Array && vm.borderWidth.length === 1 ? {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nit] could use helpers.isArray here

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would also put vm.borderWidth into a local variable to improve minification

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This whole block would be more compact and efficient if borderWidth is an object.

Copy link
Member

@simonbrunel simonbrunel left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Really nice feature but I'm not sure about the public API (using arrays to defines these properties), why not using objects instead:

  • doesn't require to specify all values to set the left side
  • it works better with the config merge process
  • easier to test values: if (borderSkipped.left)
  • it's clearer what value for what side
borderSkipped: true; // shorthand for all sides
borderSkipped: {
    left: true,
    top: true
};

borderWidth: 1; // << shorthand for all sides
borderWidth: {
    left: 2,
    right: 1
};

Will need unit tests as well :)

@@ -50,11 +50,106 @@ module.exports = function(Chart) {
}

Chart.elements.Rectangle = Chart.Element.extend({
draw: function() {
getBorderWidth: function(top, right, bottom, left) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should be marked @private

// - top-bottom + left-right
// - top + right + bottom + left
return !vm.borderWidth ? [] :
vm.borderWidth instanceof Array && vm.borderWidth.length === 1 ? {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This whole block would be more compact and efficient if borderWidth is an object.

// - top + right + bottom + left
return !vm.borderWidth ? [] :
vm.borderWidth instanceof Array && vm.borderWidth.length === 1 ? {
top: ceiling(vm.borderWidth[0], barSizeTopBottom),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not using Math.min(vm.borderWidth[0], barSizeLeftRight)?

left: ceiling(vm.borderWidth, barSizeLeftRight)
};
},
drawShape: function(borders) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should be marked @private

ctx.lineWidth = 0;
ctx.moveTo(borders.bottom[0][0], borders.bottom[0][1]);
for (var i in borders) {
if (borders.hasOwnProperty(i)) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems counter efficient to iterate on this borders object and have all these hasOwnProperty useless calls. Since it's an internal variable, why not using an array instead and convert this for..in loop to a tradition for(var i=0; i<4; ++i) loop?

},
adjustBorders: function(borders, borderWidth, borderSkipped) {
// Make sure border lengths are adjusted for border width.
if (!borderSkipped.includes('top') && borderWidth.top > 0) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Array.includes is not supported by IE (though, if borderSkipped is an object, no need for that call).

borders.right = [[borders.right[0][0], borders.right[0][1] + borderWidth.bottom/2], borders.right[1]];
}
},
drawBorders: function(borders, borderWidth, borderSkipped) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should be marked @private


ctx.strokeStyle = vm.borderColor;
for (var i in borders) {
if (borders.hasOwnProperty(i)) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here, iterating on this borders object doesn't seem efficient

// Skip drawing the border
continue;
}
ctx.beginPath();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since the strokeStyle doesn't change for each border, beginPath() and stroke() should be moved outside the loop:

ctx.strokeStyle = vm.borderColor;
ctx.beginPath();
for (...) {
    ctx.moveTo(...)
    ctx.lineTo(...)
}
ctx.stroke();

corner = cornerAt(i);
ctx.lineTo(corner[0], corner[1]);
}
var borders = {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For performance sake, this internal borders object should be an array of array (properly commented):

var borders = [
    [x0, y0, x1, y1],  //< top
    [x0, y0, x1, y1],  //< right
    [x0, y0, x1, y1],  //< bottom
    [x0, y0, x1, y1],  //< left
];

@bokysan
Copy link
Author

bokysan commented May 18, 2017

Thank you for the feedback; I'll look into optimising the code. I've been thinking borderWidth being an object though -- it does simplify things, but at the minor expense of not being able to set the top-bottom / left-right borders jointly; e.g.:

borderWidth: [ 2, 4 ] 

as opposed to

borderWidth: { top: 2, right: 4, bottom: 2, left: 4 }

Seems more brief and in line with how CSS handles it. What say you - would it be better to support both syntaxes? What's the project policy on this?

@simonbrunel
Copy link
Member

simonbrunel commented May 18, 2017

Arrays are evils when merging configs because they can't be merged (ref. scale configs). The CSS way to define TRBL is brief but not intuitive (at least there is alternatives, for example: border-left-width, etc). And as a consistency note: we already use the object form for the padding config.

That's why I think the first implementation should be entirely based on the object approach (with boolean/int shorthands). The rectangle element defaults should use the object form:

Chart.defaults.global.elements.rectangle = {
   borderWidth: { },   // undefined/null == 0
};

I agree that other forms are interesting, so we could add support for 't r b l' string or Array since it would just imply parsing to convert into the object form. But I would do that in another PR to avoid overloading this one and because it would required some refactor with the layout padding.

@etimberg thoughts?

@bokysan
Copy link
Author

bokysan commented May 18, 2017

Agreed. I'm convinced.

Just occurred to me, though -- I've just followed through with bordersSkipped, however, it's a double negative. Humans usually better comprehend positive statements, e.g., instead of

bordersSkipped: {
    bottom: true
}

it would be clearer to say:

showBorders: {
    bottom: false
}

I'm not sure how that would affect compatibility with old code, though.

@simonbrunel
Copy link
Member

simonbrunel commented May 18, 2017

That's a very good point and if I correctly understand this config, the answer is: no need for a new config, just deprecate borderSkipped because it's now redundant with borderWidth per side:

borderSkipped: 'bottom' == borderWidth: {bottom: 0}

or 

borderSkipped: ['left', 'bottom'] == borderWidth: {left: 0, bottom: 0}

or 

borderSkipped: {left: true, bottom: true} == borderWidth: {left: 0, bottom: 0}

So I would not add object/array support for borderSkipped but we need to maintain backward compatibility. I think we could simply ignore borderSkipped if borderWidth is not a number?

@etimberg
Copy link
Member

Interesting point on deprecating borderSkipped. If we do deprecate it, we should make sure to update the default configs for vertical and horizontal bar charts

@bokysan
Copy link
Author

bokysan commented May 19, 2017

I've done some initial refactoring -- using borderWidth instead of borderSkipped simplifies code a bit.

@benmccann
Copy link
Contributor

@bokysan this PR needs a rebase

@simonbrunel simonbrunel removed this from the Version 2.7 milestone Aug 28, 2017
@benmccann
Copy link
Contributor

@bokysan a reminder that this PR needs to be rebased

@etimberg
Copy link
Member

Closing as out of date

@etimberg etimberg closed this Dec 31, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants