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

Add overflow option to labels configuration #106

Merged
merged 7 commits into from
Sep 26, 2022
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions docs/samples/captions.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,14 +63,31 @@ const config = {
weight: 'bold'
},
padding: 5
},
labels: {
display: false,
overflow: 'hidden'
}
}]
},
options: options
};
// </block:config>
function toggle(chart) {
const labels = chart.data.datasets[0].labels;
labels.display = !labels.display;
chart.update();
}

const actions = [
{
name: 'Toggle labels',
handler: (chart) => toggle(chart, 'region')
stockiNail marked this conversation as resolved.
Show resolved Hide resolved
}
];

module.exports = {
config,
actions
};
```
12 changes: 10 additions & 2 deletions docs/usage.md
Original file line number Diff line number Diff line change
Expand Up @@ -193,9 +193,10 @@ The labels options can control if and how a label, to represent the data, can be
| `color` | [`Color`](https://www.chartjs.org/docs/latest/general/colors.html) | Yes | `undefined`
| `display` | `boolean` | - | `false`
| [`formatter`](#formatter) | `function` | Yes |
| [`font`](https://www.chartjs.org/docs/latest/general/fonts.html) | `Font` | Yes | `{}`
| [`font`](https://www.chartjs.org/docs/latest/general/fonts.html) | `Font` | Yes | `{}`
| `hoverColor` | [`Color`](https://www.chartjs.org/docs/latest/general/colors.html) | Yes | `undefined`
| [`hoverFont`](https://www.chartjs.org/docs/latest/general/fonts.html) | `Font` | Yes | `{}`
| [`hoverFont`](https://www.chartjs.org/docs/latest/general/fonts.html) | `Font` | Yes | `{}`
| [`overflow`](#overflow) | `string` | Yes | `cut`
| `padding` | `number` | - | `3`
| [`position`](#position) | `string` | Yes | `middle`

Expand All @@ -215,6 +216,13 @@ The align property specifies the text horizontal alignment used when drawing the
* `left`: the text is left-aligned.
* `right`: the text is right-aligned.

### Overflow

The overflow property controls what happens to a label that is too big to fit into a rectangle. The possible values are:

* `cut`: if the label is too big, it will be cut to stay inside the rectangle. It is the default.
* `hidden`: the label is removed altogether if the rectangle is too small for it.

### Position

The position property specifies the text vertical alignment used when drawing the label. The possible values are:
Expand Down
57 changes: 46 additions & 11 deletions src/element.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import {Element} from 'chart.js';
import {toFont, isArray, isObject} from 'chart.js/helpers';

const widthCache = new Map();

/**
* Helper function to get the bounds of the rect
* @param {TreemapElement} rect the rect
Expand Down Expand Up @@ -114,23 +116,55 @@ function drawCaption(ctx, rect, item) {
ctx.fillText(captionsOpts.formatter || item.g, x, rect.y + padding + spacing + (font.lineHeight / 2));
}

function measureLabelSize(ctx, lines, font) {
kurkle marked this conversation as resolved.
Show resolved Hide resolved
const mapKey = lines.join() + font.string + (ctx._measureText ? '-spriting' : '');
if (!widthCache.has(mapKey)) {
ctx.save();
ctx.font = font.string;
const count = lines.length;
let width = 0;
for (let i = 0; i < count; i++) {
const text = lines[i];
width = Math.max(width, ctx.measureText(text).width);
}
ctx.restore();
const height = count * font.lineHeight;
widthCache.set(mapKey, {width, height});
}
return widthCache.get(mapKey);
}

function labelToDraw(ctx, rect, options, {labels, font}) {
const overflow = options.overflow;
if (overflow === 'hidden') {
const padding = options.padding;
const labelSize = measureLabelSize(ctx, labels, font);
return !((labelSize.width + padding * 2) > rect.width || (labelSize.height + padding * 2) > rect.height);
}
return true;
}

function drawLabel(ctx, rect) {
const opts = rect.options;
const labelsOpts = opts.labels;
const optColor = (rect.active ? labelsOpts.hoverColor : labelsOpts.color) || labelsOpts.color;
const label = labelsOpts.formatter;
if (!label) {
return;
}
const labels = isArray(label) ? label : [label];
const optFont = (rect.active ? labelsOpts.hoverFont : labelsOpts.font) || labelsOpts.font;
const font = toFont(optFont);
const lh = font.lineHeight;
const label = labelsOpts.formatter;
if (label) {
const labels = isArray(label) ? label : [label];
const xyPoint = calculateXYLabel(opts, rect, labels, lh);
ctx.font = font.string;
ctx.textAlign = labelsOpts.align;
ctx.textBaseline = labelsOpts.position;
ctx.fillStyle = optColor;
labels.forEach((l, i) => ctx.fillText(l, xyPoint.x, xyPoint.y + i * lh));
if (!labelToDraw(ctx, rect, labelsOpts, {labels, font})) {
return;
}
const optColor = (rect.active ? labelsOpts.hoverColor : labelsOpts.color) || labelsOpts.color;
const lh = font.lineHeight;
const xyPoint = calculateXYLabel(opts, rect, labels, lh);
ctx.font = font.string;
ctx.textAlign = labelsOpts.align;
ctx.textBaseline = labelsOpts.position;
ctx.fillStyle = optColor;
labels.forEach((l, i) => ctx.fillText(l, xyPoint.x, xyPoint.y + i * lh));
}

function drawDivider(ctx, rect, item) {
Expand Down Expand Up @@ -286,6 +320,7 @@ TreemapElement.defaults = {
display: false,
formatter: (ctx) => ctx.raw.g ? [ctx.raw.g, ctx.raw.v] : ctx.raw.v,
font: {},
overflow: 'clip',
position: 'middle',
padding: 3
}
Expand Down
30 changes: 30 additions & 0 deletions test/fixtures/basic/labelsMultilineOverflowCut.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
export default {
config: {
type: 'treemap',
data: {
datasets: [{
label: 'Simple treemap',
data: [6, 6, 4, 3, 2, 2, 1],
backgroundColor: 'red',
labels: {
display: true,
overflow: 'cut',
formatter: (ctx) => ('value is ' + ctx.raw.v + ',').repeat(8).split(',')
}
}]
},
options: {
layout: {
padding: {
bottom: 10
}
}
}
},
options: {
canvas: {
height: 256,
width: 512
}
}
};
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
30 changes: 30 additions & 0 deletions test/fixtures/basic/labelsMultilineOverflowHidden.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
export default {
config: {
type: 'treemap',
data: {
datasets: [{
label: 'Simple treemap',
data: [6, 6, 4, 3, 2, 2, 1],
backgroundColor: 'red',
labels: {
display: true,
overflow: 'hidden',
formatter: (ctx) => ('value is ' + ctx.raw.v + ',').repeat(6).split(',')
}
}]
},
options: {
layout: {
padding: {
bottom: 10
stockiNail marked this conversation as resolved.
Show resolved Hide resolved
}
}
}
},
options: {
canvas: {
height: 256,
width: 512
}
}
};
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
30 changes: 30 additions & 0 deletions test/fixtures/basic/labelsOverflowCut.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
export default {
config: {
type: 'treemap',
data: {
datasets: [{
label: 'Simple treemap',
data: [6, 6, 4, 3, 2, 2, 1],
backgroundColor: 'red',
labels: {
display: true,
overflow: 'cut',
formatter: (ctx) => ('value is ' + ctx.raw.v).repeat(5)
}
}]
},
options: {
layout: {
padding: {
bottom: 10
}
}
}
},
options: {
canvas: {
height: 256,
width: 512
}
}
};
Binary file added test/fixtures/basic/labelsOverflowCut.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
30 changes: 30 additions & 0 deletions test/fixtures/basic/labelsOverflowHidden.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
export default {
config: {
type: 'treemap',
data: {
datasets: [{
label: 'Simple treemap',
data: [6, 6, 4, 3, 2, 2, 1],
backgroundColor: 'red',
labels: {
display: true,
overflow: 'hidden',
formatter: (ctx) => ('value is ' + ctx.raw.v).repeat(5)
}
}]
},
options: {
layout: {
padding: {
bottom: 10
}
}
}
},
options: {
canvas: {
height: 256,
width: 512
}
}
};
Binary file added test/fixtures/basic/labelsOverflowHidden.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions types/index.esm.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ type TreemapControllerDatasetLabelsOptions = {
font?: FontSpec,
hoverColor?: Scriptable<Color, TreemapScriptableContext>,
hoverFont?: FontSpec,
overflow?: Scriptable<LabelOverflow, TreemapScriptableContext>
padding?: number,
position?: Scriptable<LabelPosition, TreemapScriptableContext>
}
Expand All @@ -38,6 +39,8 @@ export type LabelPosition = 'top' | 'middle' | 'bottom';

export type LabelAlign = 'left' | 'center' | 'right';

export type LabelOverflow = 'cut' | 'hidden';

type TreemapControllerDatasetDividersOptions = {
display?: boolean,
lineCapStyle?: string,
Expand Down