UNPKG

@syncfusion/ej2-heatmap

Version:

Feature rich data visulization control used to visualize the matrix data where the individual values are represented as colors

1,372 lines (1,368 loc) 461 kB
import { ChildProperty, Property, Complex, Collection, isNullOrUndefined, extend, createElement, SanitizeHtmlHelper, compile, remove, Browser, merge, select, print, Component, Internationalization, EventHandler, Touch, Event, NotifyPropertyChanges } from '@syncfusion/ej2-base'; import { SvgRenderer, Tooltip as Tooltip$1, CanvasRenderer } from '@syncfusion/ej2-svg-base'; import { DataUtil } from '@syncfusion/ej2-data'; import { PdfPageOrientation, PdfDocument, SizeF, PdfBitmap } from '@syncfusion/ej2-pdf-export'; /** * Specifies HeatMaps Themes */ // eslint-disable-next-line @typescript-eslint/no-namespace var Theme; (function (Theme) { /** @private */ Theme.heatMapTitleFont = { size: '15px', fontWeight: '500', color: null, fontStyle: 'Normal', fontFamily: 'Segoe UI' }; /** @private */ Theme.titleFont = { size: '13px', fontWeight: 'Normal', color: null, fontStyle: 'Normal', fontFamily: 'Segoe UI', textOverflow: 'None' }; /** @private */ Theme.axisTitleFont = { size: '12px', fontWeight: 'Normal', color: null, fontStyle: 'Normal', fontFamily: 'Segoe UI' }; /** @private */ Theme.axisLabelFont = { size: '12px', fontWeight: 'Normal', color: null, fontStyle: 'Normal', fontFamily: 'Segoe UI', textOverflow: 'None' }; /** @private */ Theme.legendLabelFont = { size: '12px', fontWeight: 'Normal', color: null, fontStyle: 'Normal', fontFamily: 'Segoe UI', textOverflow: 'None' }; /** @private */ Theme.rectLabelFont = { size: '12px', fontWeight: 'Normal', color: null, fontStyle: 'Normal', fontFamily: 'Segoe UI', textOverflow: 'None' }; /** @private */ Theme.tooltipFont = { size: '13px', fontWeight: 'Normal', color: null, fontStyle: 'Normal', fontFamily: 'Segoe UI', textOverflow: 'None' }; })(Theme || (Theme = {})); /** * Functions to check whether target object implement specific interface. * * @param { HeatMapTheme } theme - specifies the value. * @returns { IThemeStyle } returns the theme style * @private */ function getThemeColor(theme) { let style; switch (theme.toLowerCase()) { case 'highcontrastlight': case 'highcontrast': style = { heatMapTitle: '#ffffff', axisTitle: '#ffffff', axisLabel: '#ffffff', cellBorder: '#EEEEEE', background: '#000000', cellTextColor: '#000000', toggledColor: '#000000', emptyCellColor: '#EEEEEE', legendLabel: '#ffffff', palette: [{ 'color': '#BEE7EE' }, { 'color': '#85c4cf' }, { 'color': '#4CA1AF' }] }; break; case 'materialdark': case 'fabricdark': case 'bootstrapdark': style = { heatMapTitle: '#ffffff', axisTitle: '#ffffff', axisLabel: '#DADADA', cellBorder: '#EEEEEE', background: '#000000', cellTextColor: '#000000', toggledColor: '#000000', emptyCellColor: '#EEEEEE', legendLabel: '#ffffff', palette: [{ 'color': '#BEE7EE' }, { 'color': '#85c4cf' }, { 'color': '#4CA1AF' }] }; break; case 'bootstrap4': style = { heatMapTitle: '#212529', axisTitle: '#212529', axisLabel: '#212529', cellBorder: '#E9ECEF', background: '#FFFFFF', cellTextColor: '#212529', toggledColor: '#ffffff', emptyCellColor: '#E9ECEF', legendLabel: '#212529', palette: [{ 'color': '#BEE7EE' }, { 'color': '#85c4cf' }, { 'color': '#4CA1AF' }] }; break; case 'tailwind': style = { heatMapTitle: '#374151', axisTitle: '#374151', axisLabel: '#6B7280', cellBorder: '#E5E7EB', background: 'transparent', cellTextColor: '#111827', toggledColor: 'transparent', emptyCellColor: '#E5E7EB', legendLabel: '#374151', palette: [{ 'color': '#5A61F6' }, { 'color': '#65A30D' }, { 'color': '#14B8A6' }] }; break; case 'tailwinddark': style = { heatMapTitle: '#D1D5DB', axisTitle: '#D1D5DB', axisLabel: '#9CA3AF', cellBorder: '#4B5563', background: 'transparent', cellTextColor: '#FFFFFF', toggledColor: 'transparent', emptyCellColor: '#374151', legendLabel: '#D1D5DB', palette: [{ 'color': '#8B5CF6' }, { 'color': '#22D3EE' }, { 'color': '#F87171' }] }; break; case 'tailwind3': style = { heatMapTitle: '#111827', axisTitle: '#111827', axisLabel: '#4B5563', cellBorder: '#E5E7EB', background: 'transparent', cellTextColor: '#111827', toggledColor: 'transparent', emptyCellColor: '#E5E7EB', legendLabel: '#4B5563', palette: [{ 'color': '#2F4074' }, { 'color': '#03B4B4' }, { 'color': '#0D72DE' }] }; break; case 'tailwind3dark': style = { heatMapTitle: '#FFFFFF', axisTitle: '#FFFFFF', axisLabel: '#D1D5DB', cellBorder: '#282F3C', background: 'transparent', cellTextColor: '#FFFFFF', toggledColor: 'transparent', emptyCellColor: '#282F3C', legendLabel: '#D1D5DB', palette: [{ 'color': '#8029F1' }, { 'color': '#1ABC9C' }, { 'color': '#0D72DE' }] }; break; case 'bootstrap5': style = { heatMapTitle: '#212529', axisTitle: '#212529', axisLabel: '#212529', cellBorder: 'transparent', background: 'transparent', toggledColor: '#E9ECEF', emptyCellColor: '#E9ECEF', legendLabel: '#212529', palette: [{ 'color': '#DC3545' }, { 'color': '#FFC107' }, { 'color': '#D63384' }] }; break; case 'bootstrap5dark': style = { heatMapTitle: '#DEE2E6', axisTitle: '#DEE2E6', axisLabel: '#DEE2E6', cellBorder: 'transparent', background: 'transparent', toggledColor: '#343A40', emptyCellColor: '#343A40', legendLabel: '#DEE2E6', palette: [{ 'color': '#DC3545' }, { 'color': '#FFC107' }, { 'color': '#D63384' }] }; break; case 'fluent': style = { heatMapTitle: '#201F1E', axisTitle: '#201F1E', axisLabel: '#201F1E', cellBorder: '#EDEBE9', background: 'transparent', cellTextColor: '#111827', toggledColor: 'transparent', emptyCellColor: '#EDEBE9', legendLabel: '#201F1E', palette: [{ 'color': '#EDEBE9' }, { 'color': '#614570' }, { 'color': '#4C6FB1' }] }; break; case 'fluentdark': style = { heatMapTitle: '#F3F2F1', axisTitle: '#F3F2F1', axisLabel: '#F3F2F1', cellBorder: '#EDEBE9', background: 'transparent', cellTextColor: '#FFFFFF', toggledColor: 'transparent', emptyCellColor: '#292827', legendLabel: '#F3F2F1', palette: [{ 'color': '#292827' }, { 'color': '#2A72D5' }, { 'color': '#43B786' }] }; break; case 'material3': style = { heatMapTitle: '#1C1B1F', axisTitle: '#1C1B1F', axisLabel: '#1C1B1F', cellBorder: '#C4C7C5', background: 'transparent', cellTextColor: '#1C1B1F', toggledColor: '#F6F0FB', emptyCellColor: '#F6F0FB', legendLabel: '#49454E', palette: [{ 'color': '#6200EE' }, { 'color': '#E77A16' }, { 'color': '#82C100' }] }; break; case 'material3dark': style = { heatMapTitle: '#E6E1E5', axisTitle: '#E6E1E5', axisLabel: '#E6E1E5', cellBorder: '#444746', background: 'transparent', cellTextColor: '#E6E1E5', toggledColor: '#49454F', emptyCellColor: '#49454E', legendLabel: '#CAC4D0', palette: [{ 'color': '#4EAAFF' }, { 'color': '#FA4EAB' }, { 'color': '#FFF500' }] }; break; case 'fluent2': style = { heatMapTitle: '#242424', axisTitle: '#242424', axisLabel: '#242424', cellBorder: 'transparent', background: 'transparent', cellTextColor: '#242424', toggledColor: '#EDEBE9', emptyCellColor: '#EDEBE9', legendLabel: '#242424', palette: [{ 'color': '#6200EE' }, { 'color': '#09AF74' }, { 'color': '#0076E5' }] }; break; case 'fluent2dark': case 'fluent2highcontrast': style = { heatMapTitle: '#FFFFFF', axisTitle: '#FFFFFF', axisLabel: '#FFFFFF', cellBorder: 'transparent', background: 'transparent', cellTextColor: '#FFFFFF', toggledColor: '#292827', emptyCellColor: '#292827', legendLabel: '#FFFFFF', palette: [{ 'color': '#9BB449' }, { 'color': '#2A72D5' }, { 'color': '#43B786' }] }; break; default: style = { heatMapTitle: '#424242', axisTitle: '#424242', axisLabel: '#686868', cellBorder: '#EEEEEE', cellTextColor: '#000000', toggledColor: '#ffffff', background: '#FFFFFF', emptyCellColor: '#EEEEEE', legendLabel: '#353535', palette: [{ 'color': '#BEE7EE' }, { 'color': '#85c4cf' }, { 'color': '#4CA1AF' }] }; break; } return style; } var __decorate = (undefined && undefined.__decorate) || function (decorators, target, key, desc) { var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; return c > 3 && r && Object.defineProperty(target, key, r), r; }; /** * Sets and gets the options to customize the text in heatmap. */ class Font extends ChildProperty { } __decorate([ Property('16px') ], Font.prototype, "size", void 0); __decorate([ Property('') ], Font.prototype, "color", void 0); __decorate([ Property('Segoe UI') ], Font.prototype, "fontFamily", void 0); __decorate([ Property('Normal') ], Font.prototype, "fontWeight", void 0); __decorate([ Property('Normal') ], Font.prototype, "fontStyle", void 0); __decorate([ Property('Center') ], Font.prototype, "textAlignment", void 0); __decorate([ Property('Trim') ], Font.prototype, "textOverflow", void 0); /** * Sets and gets the options to configures the margins of the heatmap. */ class Margin extends ChildProperty { } __decorate([ Property(10) ], Margin.prototype, "left", void 0); __decorate([ Property(10) ], Margin.prototype, "right", void 0); __decorate([ Property(10) ], Margin.prototype, "top", void 0); __decorate([ Property(10) ], Margin.prototype, "bottom", void 0); /** * Sets and gets the options to customize the borders in the heatmap. */ class Border extends ChildProperty { } __decorate([ Property('') ], Border.prototype, "color", void 0); __decorate([ Property(1) ], Border.prototype, "width", void 0); __decorate([ Property('') ], Border.prototype, "radius", void 0); /** * Sets and gets the options to customize the tooltip borders in the heatmap. */ class TooltipBorder extends ChildProperty { } __decorate([ Property('') ], TooltipBorder.prototype, "color", void 0); __decorate([ Property(0) ], TooltipBorder.prototype, "width", void 0); /** * Sets and gets the options to configure the mapping value for size and color in bubble cell type. */ class BubbleData extends ChildProperty { } __decorate([ Property(null) ], BubbleData.prototype, "size", void 0); __decorate([ Property(null) ], BubbleData.prototype, "color", void 0); /** * Sets and gets the options to customize the title of heatmap. */ class Title extends ChildProperty { } __decorate([ Property('') ], Title.prototype, "text", void 0); __decorate([ Complex({}, Font) ], Title.prototype, "textStyle", void 0); /** * Sets and gets the options to apply the fill color value for cell color range. */ class FillColor extends ChildProperty { } __decorate([ Property('#eeeeee') ], FillColor.prototype, "minColor", void 0); __decorate([ Property('#eeeeee') ], FillColor.prototype, "maxColor", void 0); /** * Sets and gets the options to customize palette colors. */ class PaletteCollection extends ChildProperty { } __decorate([ Property(null) ], PaletteCollection.prototype, "value", void 0); __decorate([ Property(null) ], PaletteCollection.prototype, "color", void 0); __decorate([ Property(null) ], PaletteCollection.prototype, "label", void 0); __decorate([ Property(null) ], PaletteCollection.prototype, "startValue", void 0); __decorate([ Property(null) ], PaletteCollection.prototype, "endValue", void 0); __decorate([ Property(null) ], PaletteCollection.prototype, "minColor", void 0); __decorate([ Property(null) ], PaletteCollection.prototype, "maxColor", void 0); /** * Sets and gets the options to customize the label border. */ class AxisLabelBorder extends ChildProperty { } __decorate([ Property('#b5b5b5') ], AxisLabelBorder.prototype, "color", void 0); __decorate([ Property(1) ], AxisLabelBorder.prototype, "width", void 0); __decorate([ Property('Rectangle') ], AxisLabelBorder.prototype, "type", void 0); /** * Sets and gets the options to customize the size of the bubble heatmap cell type. */ class BubbleSize extends ChildProperty { } __decorate([ Property('0%') ], BubbleSize.prototype, "minimum", void 0); __decorate([ Property('100%') ], BubbleSize.prototype, "maximum", void 0); /** * Sets and gets the options to configure the multi-level labels. */ class MultiLevelCategories extends ChildProperty { } __decorate([ Property(null) ], MultiLevelCategories.prototype, "start", void 0); __decorate([ Property(null) ], MultiLevelCategories.prototype, "end", void 0); __decorate([ Property('') ], MultiLevelCategories.prototype, "text", void 0); __decorate([ Property(null) ], MultiLevelCategories.prototype, "maximumTextWidth", void 0); /** * Sets and gets the options to customize the multi-level labels. */ class MultiLevelLabels extends ChildProperty { } __decorate([ Property('Center') ], MultiLevelLabels.prototype, "alignment", void 0); __decorate([ Property('Wrap') ], MultiLevelLabels.prototype, "overflow", void 0); __decorate([ Complex(Theme.axisLabelFont, Font) ], MultiLevelLabels.prototype, "textStyle", void 0); __decorate([ Complex({ color: '#b5b5b5', width: 1, type: 'Rectangle' }, AxisLabelBorder) ], MultiLevelLabels.prototype, "border", void 0); __decorate([ Collection([], MultiLevelCategories) ], MultiLevelLabels.prototype, "categories", void 0); /** * Internal class used to maintain colorcollection. * * @private */ class ColorCollection { constructor(value, color, label, startValue, endValue, minColor, maxColor) { this.value = value; this.color = color; this.label = label; this.startValue = startValue; this.endValue = endValue; this.minColor = minColor; this.maxColor = maxColor; } } /** * Specifies the current data of the bubble cell. */ class BubbleTooltipData { /** * @param {string} mappingName - Specifies the mapping name. * @param {number} bubbleData - Specifies the bubble data. * @param {string} valueType - Specifies the value type. * @private */ constructor(mappingName, bubbleData, valueType) { this.mappingName = mappingName; this.bubbleData = bubbleData; this.valueType = valueType; } } /** * Internal class used to maintain legend colorcollection. * * @private */ class LegendColorCollection { constructor(value, color, label, startValue, endValue, minColor, maxColor, isHidden) { this.value = value; this.color = color; this.label = label; this.startValue = startValue; this.endValue = endValue; this.minColor = minColor; this.maxColor = maxColor; this.isHidden = isHidden; } } /** * class used to maintain xAxis labels details for multipleRow label intersect action. * * @private */ class MultipleRow { constructor(start, end, index, label, row) { this.index = 1; this.row = 1; this.start = start; this.end = end; this.index = index; this.label = label; this.row = row; } } var __decorate$1 = (undefined && undefined.__decorate) || function (decorators, target, key, desc) { var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; return c > 3 && r && Object.defineProperty(target, key, r), r; }; /** * Sets and gets the options to customize the color palette of heatmap. */ class PaletteSettings extends ChildProperty { } __decorate$1([ Collection([{}], PaletteCollection) ], PaletteSettings.prototype, "palette", void 0); __decorate$1([ Property('Gradient') ], PaletteSettings.prototype, "type", void 0); __decorate$1([ Property('') ], PaletteSettings.prototype, "emptyPointColor", void 0); __decorate$1([ Property('Table') ], PaletteSettings.prototype, "colorGradientMode", void 0); __decorate$1([ Complex({}, FillColor) ], PaletteSettings.prototype, "fillColor", void 0); /** * Helper class for colormapping */ class RgbColor { constructor(r, g, b) { this.R = r; this.G = g; this.B = b; } } class CellColor { constructor(heatMap) { this.heatMap = heatMap; } /** * To convert hexa color to RGB. * * @returns {any} * @private */ convertToRGB(value, colorMapping) { let previousOffset = this.heatMap.isColorRange ? colorMapping[0].startValue : colorMapping[0].value; let nextOffset = 0; let i = 0; let previousColor; let nextColor; if (this.heatMap.isColorRange && this.heatMap.paletteSettings.type === 'Gradient') { for (i = 0; i < colorMapping.length; i++) { const offset = Number(colorMapping[i].endValue); if (value <= offset && value >= Number(colorMapping[i].startValue)) { nextOffset = offset; previousColor = this.heatMap.colorCollection[i].minColor; nextColor = this.heatMap.colorCollection[i].maxColor; } else if (colorMapping[0].startValue !== this.heatMap.dataSourceMinValue && value < colorMapping[0].startValue) { nextOffset = colorMapping[0].startValue; previousOffset = this.heatMap.dataSourceMinValue; previousColor = this.heatMap.paletteSettings.fillColor.minColor; nextColor = this.heatMap.paletteSettings.fillColor.maxColor; break; } else if (value > offset && value <= (i === (colorMapping.length - 1) ? this.heatMap.dataSourceMaxValue : colorMapping[i + 1].startValue)) { nextOffset = (i === (colorMapping.length - 1)) ? this.heatMap.dataSourceMaxValue : colorMapping[i + 1].startValue; previousOffset = offset; previousColor = this.heatMap.paletteSettings.fillColor.minColor; nextColor = this.heatMap.paletteSettings.fillColor.maxColor; break; } else { nextOffset = offset; previousOffset = offset; } } } else { for (i = 1; i < colorMapping.length; i++) { const offset = Number(colorMapping[i].value); if (value <= offset) { nextOffset = offset; previousColor = this.getEqualColor(colorMapping, previousOffset); nextColor = this.getEqualColor(colorMapping, nextOffset); break; } else { nextOffset = offset; previousOffset = offset; } } } let percent = 0; const full = (nextOffset) - previousOffset; percent = (value - previousOffset) / full; percent = isNaN(percent) || !isFinite(percent) ? 0 : percent; return this.getPercentageColor(percent, previousColor, nextColor); } /** * To convert RGB to HEX. * * @returns {string} * @private */ rgbToHex(r, g, b) { return '#' + this.componentToHex(r) + this.componentToHex(g) + this.componentToHex(b); } /** * To convert Component to HEX. * * @returns {string} * @private */ componentToHex(c) { const hex = c.toString(16); return hex.length === 1 ? '0' + hex : hex; } /** * To get similar color. * * @returns {string} * @private */ getEqualColor(list, offset) { for (let i = 0; i < list.length; i++) { if (Number(list[i].value) === offset) { let color = list[i].color; if (!isNullOrUndefined(color)) { if (color.indexOf('rgb') !== -1) { color = this.convertToHex(color); } else if (color.indexOf('#') === -1) { color = '#FFFFFF'; } } else { color = '#FFFFFF'; } return color; } } return '#00000'; } /** * To convert RGB to HEX. * * @returns {string} * @private */ convertToHex(color) { let itemColor = color.substr(3); itemColor = itemColor.split('(')[1].split(')')[0]; const colorSplit = itemColor.split(','); itemColor = this.rgbToHex(parseInt(colorSplit[0], 10), parseInt(colorSplit[1], 10), parseInt(colorSplit[2], 10)); return itemColor; } /** * To get RGB for percentage value. * * @returns {any} * @private */ getPercentageColor(percent, previous, next) { const nextColor = next.split('#')[1]; const prevColor = previous.split('#')[1]; const r = this.getPercentage(percent, parseInt(prevColor.substr(0, 2), 16), parseInt(nextColor.substr(0, 2), 16)); const g = this.getPercentage(percent, parseInt(prevColor.substr(2, 2), 16), parseInt(nextColor.substr(2, 2), 16)); const b = this.getPercentage(percent, parseInt(prevColor.substr(4, 2), 16), parseInt(nextColor.substr(4, 2), 16)); return new RgbColor(r, g, b); } /** * To convert numbet to percentage. * * @returns {any} * @private */ getPercentage(percent, previous, next) { const full = next - previous; return Math.round((previous + (full * percent))); } /** * To get complete color Collection. * * @private */ getColorCollection() { const heatMap = this.heatMap; heatMap.colorCollection = []; heatMap.legendColorCollection = []; let range; for (let j = 0; j < this.heatMap.paletteSettings.palette.length; j++) { if (this.heatMap.paletteSettings.palette[j].startValue === null || this.heatMap.paletteSettings.palette[j].endValue === null) { this.heatMap.isColorRange = false; break; } else { this.heatMap.isColorRange = true; } } const minValue = heatMap.bubbleSizeWithColor ? heatMap.minColorValue : heatMap.dataSourceMinValue; const maxValue = heatMap.bubbleSizeWithColor ? heatMap.maxColorValue : heatMap.dataSourceMaxValue; heatMap.emptyPointColor = heatMap.paletteSettings.emptyPointColor ? heatMap.paletteSettings.emptyPointColor : heatMap.themeStyle.emptyCellColor; const tempcolorMapping = this.orderbyOffset(this.heatMap.isColorRange ? heatMap.paletteSettings.palette : heatMap.paletteSettings.palette && heatMap.paletteSettings.palette.length > 1 ? heatMap.paletteSettings.palette : heatMap.themeStyle.palette); if (!tempcolorMapping.isCompact) { if (heatMap.paletteSettings.type === 'Gradient') { range = (maxValue - minValue) / (tempcolorMapping.offsets.length - 1); } else { range = (maxValue - minValue) / (tempcolorMapping.offsets.length); } if (tempcolorMapping.offsets.length >= 2) { for (let index = 0; index < tempcolorMapping.offsets.length; index++) { heatMap.colorCollection.push(new ColorCollection((Math.round(((minValue) + (index * range)) * 100) / 100), tempcolorMapping.offsets[index].color, tempcolorMapping.offsets[index].label, tempcolorMapping.offsets[index].startValue, tempcolorMapping.offsets[index].endValue, tempcolorMapping.offsets[index].minColor, tempcolorMapping.offsets[index].maxColor)); heatMap.legendColorCollection.push(new LegendColorCollection(Math.round(((minValue) + (index * range)) * 100) / 100, tempcolorMapping.offsets[index].color, tempcolorMapping.offsets[index].label, tempcolorMapping.offsets[index].startValue, tempcolorMapping.offsets[index].endValue, tempcolorMapping.offsets[index].minColor, tempcolorMapping.offsets[index].maxColor, false)); } } } else { heatMap.colorCollection = tempcolorMapping.offsets; heatMap.legendColorCollection = extend([], tempcolorMapping.offsets, null, true); } if (!this.heatMap.isColorRange) { this.updateLegendColorCollection(minValue, maxValue, tempcolorMapping); } } /** * To update legend color Collection. * * @private */ updateLegendColorCollection(minValue, maxValue, tempcolorMapping) { if (this.heatMap.paletteSettings.type === 'Fixed' && (tempcolorMapping.isCompact || tempcolorMapping.isLabel)) { return; } if (Math.round(minValue * 100) / 100 < this.heatMap.legendColorCollection[0].value) { this.heatMap.legendColorCollection.unshift(new LegendColorCollection(Math.round(minValue * 100) / 100, this.heatMap.legendColorCollection[0].color, this.heatMap.legendColorCollection[0].label, this.heatMap.legendColorCollection[0].startValue, this.heatMap.legendColorCollection[0].endValue, this.heatMap.legendColorCollection[0].minColor, this.heatMap.legendColorCollection[0].maxColor, true)); } if (Math.round(maxValue * 100) / 100 > this.heatMap.legendColorCollection[this.heatMap.legendColorCollection.length - 1].value) { this.heatMap.legendColorCollection.push(new LegendColorCollection(Math.round(maxValue * 100) / 100, this.heatMap.legendColorCollection[this.heatMap.legendColorCollection.length - 1].color, this.heatMap.legendColorCollection[this.heatMap.legendColorCollection.length - 1].label, this.heatMap.legendColorCollection[this.heatMap.legendColorCollection.length - 1].startValue, this.heatMap.legendColorCollection[this.heatMap.legendColorCollection.length - 1].endValue, this.heatMap.legendColorCollection[this.heatMap.legendColorCollection.length - 1].minColor, this.heatMap.legendColorCollection[this.heatMap.legendColorCollection.length - 1].maxColor, true)); } } /** * To get ordered palette color collection. * * @private */ orderbyOffset(offsets) { const returnCollection = new PaletterColor(); const key = this.heatMap.isColorRange ? 'to' : 'value'; const label = 'label'; returnCollection.isCompact = true; returnCollection.isLabel = true; // eslint-disable-next-line @typescript-eslint/no-explicit-any returnCollection.offsets = offsets.sort((a, b) => { if (isNullOrUndefined(a[label]) && isNullOrUndefined(b[label])) { returnCollection.isLabel = false; } if (!isNullOrUndefined(a[key]) && !isNullOrUndefined(b[key])) { return a[key] - b[key]; } else { returnCollection.isCompact = false; return a; } }); if (!returnCollection.isCompact) { returnCollection.offsets = this.heatMap.paletteSettings.palette && this.heatMap.paletteSettings.palette.length > 1 ? this.heatMap.paletteSettings.palette : this.heatMap.themeStyle.palette; } return returnCollection; } /** * To get color depends to value. * * @private */ getColorByValue(text) { let color = ''; let rbg; let compareValue = 0; if (text.toString() !== '') { if (this.heatMap.cellSettings.tileType === 'Bubble' && (this.heatMap.cellSettings.bubbleType === 'Size' || this.heatMap.cellSettings.bubbleType === 'Sector')) { color = this.heatMap.isColorRange ? this.heatMap.colorCollection[0].minColor : this.heatMap.colorCollection[0].color; } else if (this.heatMap.paletteSettings.type === 'Fixed') { for (let y = 0; y < this.heatMap.colorCollection.length; y++) { compareValue = this.heatMap.isColorRange ? this.heatMap.paletteSettings.palette[y].startValue : this.heatMap.colorCollection[y + 1] ? this.heatMap.colorCollection[y + 1].value : this.heatMap.colorCollection[y].value; const singleValue = this.heatMap.dataSourceMinValue === this.heatMap.dataSourceMaxValue; if (this.heatMap.isColorRange) { let legendRange; if ((text <= this.heatMap.colorCollection[y].endValue && text >= this.heatMap.colorCollection[y].startValue)) { if (this.heatMap.legendVisibilityByCellType) { legendRange = this.heatMap.legendModule.legendRange; } color = (this.heatMap.legendVisibilityByCellType && legendRange[y] && !legendRange[y].visible) ? this.heatMap.themeStyle.toggledColor : this.heatMap.colorCollection[y].minColor; } else if (color === '') { color = this.heatMap.paletteSettings.fillColor.minColor; } } else { if ((text <= compareValue && singleValue && y === 0) || text < compareValue || (text >= compareValue && y === this.heatMap.colorCollection.length - 1)) { let legendRange; if (this.heatMap.legendVisibilityByCellType) { legendRange = this.heatMap.legendModule.legendRange; } color = (this.heatMap.legendVisibilityByCellType && legendRange[y] && !legendRange[y].visible) ? this.heatMap.themeStyle.toggledColor : this.heatMap.colorCollection[y].color; break; } } } } else { if (this.heatMap.paletteSettings.colorGradientMode !== 'Table') { this.getColorCollection(); } if (text < this.heatMap.colorCollection[0].value && !this.heatMap.isColorRange) { color = this.heatMap.colorCollection[0].color; } else if (text > this.heatMap.colorCollection[this.heatMap.colorCollection.length - 1].value && !this.heatMap.isColorRange) { color = this.heatMap.colorCollection[this.heatMap.colorCollection.length - 1].color; } else { rbg = this.convertToRGB(text, this.heatMap.colorCollection); color = this.rgbToHex(rbg.R, rbg.G, rbg.B); } } } else { color = this.heatMap.emptyPointColor; } return color; } /** * @returns {void} * @private */ destroy() { this.heatMap = null; } } /** * Function to check whether target object implement specific interface * * @param {string} value - specifies the value * @param {number} containerSize - specifies the containerSize * @returns {number} returns the number * @hidden */ function stringToNumber(value, containerSize) { if (value !== null && value !== undefined) { return value.indexOf('%') !== -1 ? (containerSize / 100) * parseInt(value, 10) : parseInt(value, 10); } return null; } /** * Function to check whether target object implement specific interface * * @param {string} text - specifies the text * @param {FontModel} font - specifies the font * @returns {Size} returns the number * @hidden */ function measureText(text, font) { const breakText = text || ''; let htmlObject = document.getElementById('heatmapmeasuretext'); if (htmlObject === null) { htmlObject = createElement('text', { id: 'heatmapmeasuretext' }); document.body.appendChild(htmlObject); } if (typeof (text) === 'string' && (text.indexOf('<') > -1 || text.indexOf('>') > -1)) { const textArray = text.split(' '); for (let i = 0; i < textArray.length; i++) { if (textArray[i].indexOf('<br/>') === -1) { textArray[i] = textArray[i].replace(/[<>]/g, '&'); } } text = textArray.join(' '); } htmlObject.innerText = (breakText.indexOf('<br>') > -1 || breakText.indexOf('<br/>') > -1) ? breakText : text; htmlObject.style.position = 'absolute'; htmlObject.style.visibility = 'hidden'; htmlObject.style.fontSize = (font.size).indexOf('px') !== -1 ? font.size : font.size + 'px'; htmlObject.style.fontWeight = font.fontWeight; htmlObject.style.fontStyle = font.fontStyle; htmlObject.style.fontFamily = font.fontFamily; htmlObject.style.top = '-100'; htmlObject.style.left = '0'; htmlObject.style.whiteSpace = 'nowrap'; // For bootstrap line height issue htmlObject.style.lineHeight = 'normal'; const clientWidth = htmlObject.clientWidth; const clientHeight = htmlObject.clientHeight; removeMeasureElement(); return new Size(clientWidth, clientHeight); } /** @private */ class TextElement { constructor(fontModel, fontColor) { this['font-size'] = fontModel.size; this['font-style'] = fontModel.fontStyle.toLowerCase(); this['font-family'] = fontModel.fontFamily; this['font-weight'] = fontModel.fontWeight.toLowerCase(); this.fill = fontColor ? fontColor : ''; } } /** * Function to check whether target object implement specific interface * * @param {number} width - specifies the text * @param {number} leftPadding - specifies the font * @param {number} rightPadding - specifies the font * @param {FontModel} titleStyle - specifies the font * @returns {number} returns the number * @hidden */ function titlePositionX(width, leftPadding, rightPadding, titleStyle) { let positionX; if (titleStyle.textAlignment === 'Near') { positionX = leftPadding; } else if (titleStyle.textAlignment === 'Center') { positionX = leftPadding + width / 2; } else { positionX = width + leftPadding; } return positionX; } /** * Specifies the size information of an element. */ class Size { /** * @param {number} width - Specifies the width. * @param {number} height - Specifies the height. * @private */ constructor(width, height) { this.width = width; this.height = height; } } /** @private */ class CustomizeOption { constructor(id) { this.id = id; } } /** @private */ class PathOption extends CustomizeOption { constructor(id, fill, width, color, opacity, dashArray, d) { super(id); this.opacity = opacity; this.fill = fill; this.stroke = color ? color : ''; this['stroke-width'] = parseFloat(width.toString()); this['stroke-dasharray'] = dashArray; this.d = d; } } /** * Function to check whether target object implement specific interface * * @param { string | Function } template - Specifies the template * @param { HeatMap } heatMap - Specifies the heatmap * @param { HTMLElement } labelTemplate - Specifies the label template * @param { any } rectPosition - Specifies the rect datas * @param { any } xLabels - Specifies the xlabels * @param { any } yLabels - Specifies the ylabels * @param { number } index - Specifies the index * @returns {any} templateFunction - returns the size * @private */ // eslint-disable-next-line @typescript-eslint/no-explicit-any function createLabelTemplate(template, heatMap, labelTemplate, rectPosition, // eslint-disable-next-line @typescript-eslint/no-explicit-any xLabels, yLabels, index) { if (heatMap.enableHtmlSanitizer && typeof template === 'string') { template = SanitizeHtmlHelper.sanitize(template); } // eslint-disable-next-line @typescript-eslint/no-explicit-any const templateFunction = getTemplateFunction(template, heatMap); let rectData = null; const dataSource = heatMap.dataSource; if (heatMap.dataSourceSettings.isJsonData && (heatMap.dataSourceSettings.adaptorType === 'Cell' || heatMap.dataSourceSettings.adaptorType === 'Table')) { // eslint-disable-next-line @typescript-eslint/tslint/config const yLabelData = heatMap.yAxis.valueType === 'Numeric' ? heatMap.yAxis.labels : yLabels; // eslint-disable-next-line @typescript-eslint/tslint/config const xLabelData = heatMap.xAxis.valueType === 'Numeric' ? heatMap.xAxis.labels : xLabels; // eslint-disable-next-line @typescript-eslint/tslint/config dataSource.forEach((item) => { // eslint-disable-next-line @typescript-eslint/tslint/config const yDataMapping = heatMap.dataSourceSettings.adaptorType === 'Cell' ? Object.keys(item).some((key) => item[key] === yLabelData[rectPosition.yIndex]) : (Object.prototype.hasOwnProperty.call(item, yLabelData[rectPosition.yIndex])); if (Object.keys(item).some((key) => item[key] === xLabelData[rectPosition.xIndex]) && yDataMapping) { rectData = item; } }); } else { rectData = { value: rectPosition.value, xLabel: xLabels[rectPosition.xIndex], yLabel: yLabels[rectPosition.yIndex] }; } if (!isNullOrUndefined(templateFunction)) { const tempElement = templateFunction(rectData, heatMap, templateFunction, heatMap.element.id + '_Template' + index, false); const templateElement = convertElement(tempElement, heatMap.element.id + '_LabelTemplate_' + index); templateElement.style.cssText = 'opacity: 1; display: flex; align-items: center; justify-content: center; z-index: 2; position: absolute;' + 'top:' + rectPosition.y + 'px;' + 'left:' + rectPosition.x + 'px;' + 'height:' + rectPosition.height + 'px;' + 'width:' + rectPosition.width + 'px;'; for (let i = 0; i < templateElement.children.length; i++) { templateElement.children[i].style.pointerEvents = 'none'; } labelTemplate.appendChild(templateElement); } return labelTemplate; } /** * Function to check whether target object implement specific interface * * @param { string | Function } template - Specifies the template * @param { HeatMap } heatMap - Specifies the heatmap * @returns {any} - returns the size * @private */ // eslint-disable-next-line @typescript-eslint/no-explicit-any function getTemplateFunction(template, heatMap) { // eslint-disable-next-line @typescript-eslint/no-explicit-any let templateFn = null; try { if (typeof template !== 'function' && document.querySelectorAll(template).length) { templateFn = compile(document.querySelector(template).innerHTML.trim()); // eslint-disable-next-line @typescript-eslint/no-explicit-any } else if (heatMap.isVue || heatMap.isVue3) { templateFn = compile(template); } else if (typeof template === 'function') { templateFn = compile(template); } } catch (e) { templateFn = compile(template); } return templateFn; } /** * Function to check whether target object implement specific interface * * @param { HTMLCollection } element - Specifies the heatmap * @param { string } elementId - Specifies the template * @returns { HTMLElement } - returns the size * @private */ function convertElement(element, elementId) { const childElement = createElement('div', { id: elementId }); childElement.style.cssText = 'position: absolute;pointer-events: auto;'; let elementLength = element.length; while (elementLength > 0) { childElement.appendChild(element[0]); elementLength--; } return childElement; } /** * Class to define currentRect private property. * * @private */ class CurrentRect { constructor(x, y, width, height, value, id, xIndex, yIndex, xValue, yValue, visible, displayText, textId, allowCollection) { this.x = x; this.y = y; this.width = width; this.height = height; this.value = value; this.id = id; this.xIndex = xIndex; this.yIndex = yIndex; this.xValue = xValue; this.yValue = yValue; this.visible = visible; this.displayText = displayText; this.textId = textId; /** @private */ this.allowCollection = allowCollection; } } /** * Specifies the details of the selected cells. */ class SelectedCellDetails { /** * @param {number | BubbleTooltipData} value - Specifies the value. * @param {string} xLabel - Specifies the x label. * @param {string} yLabel - Specifies the y label. * @param {number} xValue - Specifies the x value. * @param {number} yValue - Specifies the y value. * @param {Element} cellElement - Specifies the cell element. * @param {number} xPosition - Specifies the x position. * @param {number} yPosition - Specifies the y position. * @param {number} width - Specifies the width. * @param {number} height - Specifies the height. * @param {number} x - Specifies the x value. * @param {number} y - Specifies the y value. * @private */ constructor(value, xLabel, yLabel, xValue, yValue, cellElement, xPosition, yPosition, width, height, x, y) { this.value = value; this.xLabel = xLabel; this.yLabel = yLabel; this.xValue = xValue; this.yValue = yValue; this.cellElement = cellElement; this.xPosition = xPosition; this.yPosition = yPosition; this.width = width; this.height = height; this.x = x; this.y = y; } } /** * Class to define property to draw rectangle. * * @private */ class RectOption extends PathOption { constructor(id, fill, border, opacity, rect, borderColor, rx, ry, transform, dashArray) { super(id, fill, border.width, borderColor, opacity, dashArray); this.y = rect.y; this.x = rect.x; this.height = rect.height > 0 ? rect.height : 0; this.width = rect.width > 0 ? rect.width : 0; this.rx = rx ? rx : 0; this.ry = ry ? ry : 0; this.transform = transform ? transform : ''; } } /** * Class to define property to draw circle. * * @private */ class CircleOption extends PathOption { constructor(id, fill, border, opacity, borderColor, cx, cy, r) { super(id, fill, border.width, borderColor, opacity); this.cx = cx ? cx : 0; this.cy = cy ? cy : 0; this.r = r ? r : 0; } } /** * Helper Class to define property to draw rectangle. * * @private */ class Rect { constructor(x, y, width, height) { this.x = x; this.y = y; this.width = width; this.height = height; } } /** * Class to define property to draw text. * * @private */ class TextOption extends TextElement { constructor(id, basic, element, fontColor) { super(element, fontColor); this.transform = ''; this['dominant-baseline'] = 'auto'; this.role = 'region'; this.labelRotation = 0; this.baseline = 'auto'; this.id = id; this.x = basic.x; this.y = basic.y; this['text-anchor'] = basic['text-anchor']; this.text = basic.text; this['aria-label'] = basic.text; this.transform = basic.transform; this.labelRotation = basic.labelRotation; this['dominant-baseline'] = basic['dominant-baseline']; this.baseline = basic.baseline; this.dy = basic.dy; } } /** * Helper Class to define property to draw text. * * @private */ class TextBasic { constructor(x, y, anchor, text, labelRotation, transform, baseLine, dy) { this.transform = ''; this['dominant-baseline'] = 'auto'; this.labelRotation = 0; this.baseline = 'auto'; this.x = x ? x : 0; this.y = y ? y : 0; this['text-anchor'] = anchor ? anchor : 'start'; this.text = text ? text : ''; this['aria-label'] = text; this.transform = transform ? transform : ''; this.labelRotation = labelRotation; this['dominant-baseline'] = baseLine ? baseLine : 'auto'; this.baseline = baseLine ? baseLine : ''; this.dy = dy ? dy : ''; } } /** * Class to define property to draw line. * * @private */ class Line { constructor(x1, y1, x2, y2) { this.x1 = x1; this.y1 = y1; this.x2 = x2; this.y2 = y2; } } /** * Class to define property to draw line. * * @private */ class LineOption extends PathOption { constructor(id, line, stroke, strokewidth, opacity, dasharray) { super(id, null, strokewidth, stroke, opacity, dasharray, null); this.x1 = line.x1; this.y1 = line.y1; this.x2 = line.x2; this.y2 = line.y2; } } /** * Properties required to render path. * * @private */ class PathAttributes extends PathOption { constructor(id, path, fill, border, borderWidth, opacity, borderColor) { super(id, fill, borderW