import { HTMLTooltip } from "./HTMLTooltip";
import { StyledTable } from "./StyledTable";
export class BreakdownTable extends StyledTable {
protected _table;
protected _tbody;
protected _tooltip;
constructor() {
super();
}
protected transformData() {
const rowCount = this.useCalculatedRowCount() ? this.calculateRowCount() : this.rowCount();
return this.breakdownData(rowCount);
}
protected breakdownData(limit: number): any[] {
const len = this.data().length;
const sum = this.data().reduce((acc, row) => acc + row[1], 0);
const data = [];
let percSum = 0;
this.data().sort((a, b) => a[1] > b[1] ? -1 : 1);
const hiddenRowCount = len - limit;
const showOther = hiddenRowCount > 0;
this.data()
.filter((_, i) => showOther ? i < limit - 1 : true)
.forEach(row => {
const perc = Math.round((row[1] / sum) * 100);
percSum += perc;
data.push([row[0], perc + "%"]);
});
if (showOther) {
const otherLabel = `${this.otherLabel()} (${len - limit + 1})`;
const otherPercentage = "~" + (100 - percSum) + "%";
data.push([otherLabel, otherPercentage]);
}
return data;
}
protected calculateRowCount(): number {
const theadRowHeight = this.columns().length > 0 ? this.thFontSize() + 5 : 0;
const tbodyRowHeight = this.fontSize() + 5;
const tbodyAvailableHeight = this.height() - theadRowHeight;
const rowCount = Math.floor(tbodyAvailableHeight / tbodyRowHeight);
return rowCount;
}
enter(domNode, element) {
super.enter(domNode, element);
this._tooltip = new HTMLTooltip()
.target(domNode)
;
this._tooltip
.tooltipHTML(data => {
const rowCount = this.useCalculatedRowCount() ? this.calculateRowCount() : this.rowCount();
const rowHeight = this.fontSize();
const widestLabel = Math.max(...data.map(row => this.textSize(row[0], "Verdana", this.fontSize()).width));
const widestPerc = 30;
const colCount = 2;
const w = colCount * (widestLabel + widestPerc) + (this._tooltip.padding() * 2);
const h = rowHeight * Math.ceil((data.length - rowCount) / colCount) + (this._tooltip.padding() * 2);
this._tooltip.tooltipWidth(w);
this._tooltip.tooltipHeight(h);
const otherData = this.breakdownData(this.data().length).slice(rowCount - 1);
return `
${
otherData.map(row => `
${row[0]}: ${row[1]}
`
).join("")
}
`;
})
;
}
update(domNode, element) {
this.theadColumnStyles_default([
{
"color": this.thFirstColor(),
"font-size": this.thFontSize() + "px",
"font-weight": this.thFontWeight(),
"text-align": this.labelAlignment(),
"width": "auto",
"padding": "0px"
},
{
"width": "1%",
"font-size": this.thFontSize() + "px",
"font-weight": this.thFontWeight(),
"text-align": this.percentageAlignment(),
"padding": "0px"
}
]);
this.tbodyColumnStyles_default([
{
"color": this.topLabelColor(),
"font-size": this.fontSize() + "px",
"font-weight": "normal",
"text-align": this.labelAlignment(),
"width": "auto",
"padding": "0px"
},
{
"color": this.topPercentageColor(),
"font-size": this.fontSize() + "px",
"font-weight": "normal",
"text-align": this.percentageAlignment(),
"width": "1%",
"padding": "0px"
}
]);
this.lastRowStyles_default([
{
"color": this.otherLabelColor(),
"font-size": this.fontSize() + "px",
"font-weight": this.otherLabelBold() ? "bold" : "normal",
"text-align": this.labelAlignment(),
"width": "auto",
"padding": "0px"
},
{
"color": this.otherLabelColor(),
"font-size": this.fontSize() + "px",
"font-weight": this.otherPercentageBold() ? "bold" : "normal",
"text-align": this.percentageAlignment(),
"width": "1%",
"padding": "0px"
}
]);
super.update(domNode, element);
const rowCount = this.useCalculatedRowCount() ? this.calculateRowCount() : this.rowCount();
if (rowCount < this.data().length) {
const lastRow = element.select("tbody > tr:last-child");
const context = this;
lastRow
.on("mouseout.tooltip", d => {
context._tooltip._triggerElement = lastRow;
context._tooltip
.visible(false)
.render()
;
})
.on("mouseenter.tooltip", d => {
context._tooltip._triggerElement = lastRow;
context._tooltip
.direction("n")
.data(context.data())
.visible(true)
.render()
;
})
;
}
}
}
BreakdownTable.prototype._class += " html_BreakdownTable";
export interface BreakdownTable {
useCalculatedRowCount(): boolean;
useCalculatedRowCount(_: boolean): this;
rowCount(): number;
rowCount(_: number): this;
fontSize(): number;
fontSize(_: number): this;
thFirstColor(): string;
thFirstColor(_: string): this;
thLastColor(): string;
thLastColor(_: string): this;
thFontSize(): number;
thFontSize(_: number): this;
thFontWeight(): string;
thFontWeight(_: string): this;
labelAlignment(): "left" | "center" | "right";
labelAlignment(_: "left" | "center" | "right"): this;
percentageAlignment(): "left" | "center" | "right";
percentageAlignment(_: "left" | "center" | "right"): this;
topLabelColor(): string;
topLabelColor(_: string): this;
topPercentageColor(): string;
topPercentageColor(_: string): this;
topPercentageBold(): boolean;
topPercentageBold(_: boolean): this;
otherLabel(): string;
otherLabel(_: string): this;
otherLabelColor(): string;
otherLabelColor(_: string): this;
otherLabelBold(): boolean;
otherLabelBold(_: boolean): this;
otherPercentageColor(): string;
otherPercentageColor(_: string): this;
otherPercentageBold(): boolean;
otherPercentageBold(_: boolean): this;
}
BreakdownTable.prototype.publish("useCalculatedRowCount", true, "boolean", "If true, rowCount will be calculated and its default will be overwritten");
BreakdownTable.prototype.publish("rowCount", 5, "number", "Number of total rows to display (including the 'other' row)", undefined, { disable: w => w.useCalculatedRowCount() });
BreakdownTable.prototype.publish("fontSize", 14, "number", "Font size (pixels)");
BreakdownTable.prototype.publish("labelAlignment", "left", "set", "Alignment of the label column text", ["left", "center", "right"]);
BreakdownTable.prototype.publish("percentageAlignment", "center", "set", "Alignment of the percentage column text", ["left", "center", "right"]);
BreakdownTable.prototype.publish("topLabelColor", "#333", "html-color", "Color of displayed 'top' labels");
BreakdownTable.prototype.publish("topPercentageColor", "#1A99D5", "html-color", "Color of displayed 'top' percentages");
BreakdownTable.prototype.publish("topPercentageBold", true, "html-color", "If true, the 'top' percentages will be bold");
BreakdownTable.prototype.publish("otherLabel", "Other", "string", "Label text for the 'other' row");
BreakdownTable.prototype.publish("otherLabelColor", "#AAA", "html-color", "Color of the 'other' label");
BreakdownTable.prototype.publish("otherLabelBold", false, "html-color", "If true, the 'other' label will be bold");
BreakdownTable.prototype.publish("otherPercentageColor", "#AAA", "html-color", "Color of the 'other' percentage");
BreakdownTable.prototype.publish("otherPercentageBold", false, "html-color", "If true, the 'other' percentage will be bold");
BreakdownTable.prototype.publish("thFontWeight", "bold", "string", "Font weight for th elements");
BreakdownTable.prototype.publish("thFontSize", 26, "number", "Font size for th elements");
BreakdownTable.prototype.publish("thFirstColor", "#333", "html-color", "Text color of the first th element");
BreakdownTable.prototype.publish("thLastColor", "#333", "html-color", "Text color of the last th element");