veritatisquam
Version:
Carbon charting components, lerna monorepo
224 lines (188 loc) • 6.65 kB
text/typescript
// Internal Imports
import { Title } from './title';
import { DOMUtils } from '../../services';
import { Tools } from '../../tools';
import { Statuses } from './../../interfaces/enums';
import * as Configuration from '../../configuration';
export class MeterTitle extends Title {
type = 'meter-title';
render() {
const dataset = this.model.getDisplayData();
const options = this.getOptions();
const svg = this.getContainerSVG();
const { groupMapsTo } = options.data;
// the title for a meter, is the label for that dataset
const title = svg
.selectAll('text.meter-title')
.data([dataset[groupMapsTo]]);
title
.enter()
.append('text')
.classed('meter-title', true)
.merge(title)
.attr('x', 0)
.attr('y', '1em')
.text((d) => d);
title.exit().remove();
// appends the associated percentage after title
this.appendPercentage();
// if status ranges are provided (custom or default), display indicator
this.displayStatus();
// get the max width of a title (with consideration for the status/percentage)
const maxWidth = this.getMaxTitleWidth();
const titleElement = DOMUtils.appendOrSelect(svg, 'text.meter-title');
if (
maxWidth > 0 &&
titleElement.node().getComputedTextLength() > maxWidth
) {
this.truncateTitle(titleElement, maxWidth);
}
}
/**
* Appends the corresponding status based on the value and the peak.
*/
displayStatus() {
const self = this;
const svg = this.getContainerSVG();
const options = this.getOptions();
const containerBounds = DOMUtils.getSVGElementSize(
this.services.domUtils.getMainSVG(),
{ useAttr: true }
);
// need to check if the width is 0, and try to use the parent attribute
// this can happen if the chart is toggled on/off and the height is 0 for the parent, it wont validateDimensions
const containerWidth = containerBounds.width
? containerBounds.width
: this.parent.node().getAttribute('width');
// get the status from the model
const status = this.model.getStatus();
const radius = Configuration.meter.status.indicatorSize / 2;
// create a group for the icon/inner path
const statusGroup = DOMUtils.appendOrSelect(svg, `g.status-indicator`)
.attr(
'class',
status !== null ? `status-indicator status--${status}` : ''
)
.attr('transform', `translate(${containerWidth - radius}, 0)`);
const data = status ? [status] : [];
const icon = statusGroup.selectAll('circle.status').data(data);
icon.enter()
.append('circle')
.merge(icon)
.attr('class', 'status')
.attr('r', radius)
.attr('cx', 0)
.attr('cy', `calc(1em / 2)`);
const innerIcon = statusGroup.selectAll('path.innerFill').data(data);
innerIcon
.enter()
.append('path')
.merge(innerIcon)
.attr('d', self.getStatusIconPathString(status))
.attr('transform', `translate(-${radius}, 0)`)
.attr('class', 'innerFill');
innerIcon.exit().remove();
icon.exit().remove();
}
/**
* Appends the associated percentage to the end of the title
*/
appendPercentage() {
const dataValue = this.model.getDisplayData().value;
// use the title's position to append the percentage to the end
const svg = this.getContainerSVG();
const title = DOMUtils.appendOrSelect(svg, 'text.meter-title');
// check if it is enabled
const data =
Tools.getProperty(
this.getOptions(),
'meter',
'statusBar',
'percentageIndicator',
'enabled'
) === true
? [dataValue]
: [];
// append a percentage if it is enabled, update it
const percentage = svg.selectAll('text.percent-value').data(data);
// the horizontal offset of the percentage value from the title
const offset = Configuration.meter.statusBar.paddingRight;
percentage
.enter()
.append('text')
.classed('percent-value', true)
.merge(percentage)
.text((d) => `${d}%`)
.attr(
'x',
+title.attr('x') + title.node().getComputedTextLength() + offset
) // set the position to after the title
.attr('y', title.attr('y'));
percentage.exit().remove();
}
/**
* Uses the parent class truncate logic
* @param title d3 selection of title element that will be truncated
* @param titlestring the original string that needs truncation
* @param maxWidth the max width the title can take
*/
truncateTitle(title, maxWidth) {
super.truncateTitle(title, maxWidth);
// update the position on the percentage to be inline with the title
const tspan = DOMUtils.appendOrSelect(this.parent, 'tspan');
const offset = Configuration.meter.statusBar.paddingRight;
const tspanLength = Math.ceil(tspan.node().getComputedTextLength());
const percentage = DOMUtils.appendOrSelect(
this.parent,
'text.percent-value'
);
percentage.attr(
'x',
+title.attr('x') +
title.node().getComputedTextLength() +
tspanLength +
offset
);
}
// computes the maximum space a title can take
protected getMaxTitleWidth() {
// get a reference to the title elements to calculate the size the title can be
const containerBounds = DOMUtils.getSVGElementSize(
this.services.domUtils.getMainSVG(),
{ useAttr: true }
);
// need to check if the width is 0, and try to use the parent attribute
const containerWidth = containerBounds.width
? containerBounds.width
: this.parent.node().getAttribute('width');
const percentage = DOMUtils.appendOrSelect(
this.parent,
'text.percent-value'
);
// the title needs to fit the width of the container without crowding the status, and percentage value
const offset = Configuration.meter.statusBar.paddingRight;
const percentageWidth = percentage.node().getComputedTextLength();
const statusGroup = DOMUtils.appendOrSelect(
this.parent,
'g.status-indicator'
).node();
const statusWidth =
DOMUtils.getSVGElementSize(statusGroup, { useBBox: true }).width +
Configuration.meter.status.paddingLeft;
return containerWidth - percentageWidth - offset - statusWidth;
}
/**
* Get the associated status icon for the data
* @param status the active status for the meter chart
*/
protected getStatusIconPathString(status) {
switch (status) {
case Statuses.SUCCESS:
return 'M6.875 11.3125 3.75 8.1875 4.74375 7.25 6.875 9.34375 11.50625 4.75 12.5 5.7375 Z';
case Statuses.DANGER:
return 'M10.7 11.5 4.5 5.3 5.3 4.5 11.5 10.7 Z';
case Statuses.WARNING:
return 'M7.9375,11.125 C7.41973305,11.125 7,11.544733 7,12.0625 C7,12.580267 7.41973305,13 7.9375,13 C8.45526695,13 8.875,12.580267 8.875,12.0625 C8.875,11.544733 8.45526695,11.125 7.9375,11.125 M7.3125, 3 8.5625, 3 8.5625, 9.875 7.3125, 9.875, 7.3125, 3 Z';
}
}
}