veritatisquam
Version:
Carbon charting components, lerna monorepo
119 lines (103 loc) • 3.43 kB
text/typescript
// Internal Imports
import { Component } from '../component';
import { DOMUtils } from '../../services';
import { Events } from './../../interfaces';
export class Title extends Component {
type = 'title';
render() {
const svg = this.getContainerSVG();
const text = svg
.selectAll('text.title')
.data([this.getOptions().title]);
text.enter()
.append('text')
.classed('title', true)
.merge(text)
.attr('x', 0)
.attr('y', '1em')
.html((d) => d);
// check the max space the title has to render
const maxWidth = this.getMaxTitleWidth();
const title = DOMUtils.appendOrSelect(svg, 'text.title');
// check if title needs truncation (and tooltip support)
if (title.node().getComputedTextLength() > maxWidth && maxWidth > 0) {
this.truncateTitle(title, maxWidth);
}
text.exit().remove();
}
/**
* Truncates title creating ellipses and attaching tooltip for exposing full title.
*/
truncateTitle(title, maxWidth) {
// sanity check to prevent stack overflow on binary search
if (maxWidth <= 0) {
return;
}
const untruncatedTitle = title.text();
// check if the title is too big for the containing svg
if (title.node().getComputedTextLength() > maxWidth) {
// append the ellipses to their own tspan to calculate the text length
title.append('tspan').text('...');
// get the bounding width including the elipses '...'
const tspanLength = DOMUtils.appendOrSelect(title, 'tspan')
.node()
.getComputedTextLength();
// with elipses
const titleString = title.text();
// get the index for creating the max length substring that fit within the svg
// use one less than the index to avoid crowding (the elipsis)
const substringIndex = this.getSubstringIndex(
title.node(),
0,
titleString.length - 1,
maxWidth - tspanLength
);
// use the substring as the title
title
.html(titleString.substring(0, substringIndex - 1))
.append('tspan')
.text('...');
// add events for displaying the tooltip with the title
const self = this;
title
.on('mouseover', function () {
self.services.events.dispatchEvent(Events.Tooltip.SHOW, {
hoveredElement: title,
content: untruncatedTitle,
});
})
.on('mousemove', function () {
self.services.events.dispatchEvent(Events.Tooltip.MOVE);
})
.on('mouseout', function () {
self.services.events.dispatchEvent(Events.Tooltip.HIDE);
});
}
}
// computes the maximum space a title can take
protected getMaxTitleWidth() {
return DOMUtils.getSVGElementSize(this.parent.node(), {
useAttr: true,
}).width;
}
/**
* Returns the index for a maximum length substring that is less than the width parameter.
* @param title the title node used for getting the text lengths of substrings
* @param start the start index for the binary search
* @param end the end index for the binary search
* @param width the width of the svg container that holds the title
*/
protected getSubstringIndex(title, start, end, width) {
const mid = Math.floor((end + start) / 2);
if (title.getSubStringLength(0, mid) > width) {
return this.getSubstringIndex(title, start, mid, width);
} else if (title.getSubStringLength(0, mid) < width) {
if (title.getSubStringLength(0, mid + 1) > width) {
return mid;
}
return this.getSubstringIndex(title, mid, end, width);
} else {
return mid;
}
}
}