UNPKG

mstr-viz

Version:

A new dev tool for creating custom visualizations

281 lines (250 loc) 10.8 kB
import { adjustProperties } from "./properties/adjustProperties"; import { getDefaultProperties } from "./properties/getDefaultProperties"; import { enhanceHost } from "./utils/enhanceHost"; mstrmojo.requiresDescsWPrefix("<%= pluginName %>.", 10, 11, 12, 13, 14); // mojo module mstrmojo.requiresCls("mstrmojo.CustomVisBase", "mstrmojo.customviz.CustomVisEnums"); const { GraphicModel } = mstrmojo.customviz; const { ENUM_RAW_DATA_FORMAT } = mstrmojo.models.template.DataInterface; const $CustomVisEnums = mstrmojo.customviz.CustomVisEnums; const $ENUM_LEGEND_PROPS = $CustomVisEnums.ENUM_LEGEND_PROPS; mstrmojo.plugins.<%= pluginName %>.<%= pluginName %> = mstrmojo.declare( mstrmojo.CustomVisBase, null, { scriptClass: "mstrmojo.plugins.<%= pluginName %>.<%= pluginName %>", cssClass: "<% print(_.toLower(pluginName)) %>", errorMessage: "Either there is not enough data to display the visualization or the visualization configuration is incomplete.", errorDetails: "This visualization requires one or more attributes and one metric.", useRichTooltip: false, reuseDOMNode: false, draggable: false, supportNEE: true, enableLegend: true, init(props) { this._super(props); enhanceHost(this); const defaultPropertyValues = getDefaultProperties(); let legendProps; legendProps = defaultPropertyValues[$CustomVisEnums.LEGEND_PROPERTY_SET] = {}; legendProps[$ENUM_LEGEND_PROPS.LEGEND_FONT_FAMILY] = 'Open Sans'; legendProps[$ENUM_LEGEND_PROPS.LEGEND_FONT_WEIGHT] = false; legendProps[$ENUM_LEGEND_PROPS.LEGEND_FONT_ITALIC] = false; legendProps[$ENUM_LEGEND_PROPS.LEGEND_FONT_UNDERLINE] = false; legendProps[$ENUM_LEGEND_PROPS.LEGEND_FONT_LINE_THROUGH] = false; legendProps[$ENUM_LEGEND_PROPS.LEGEND_FONT_SIZE] = '12pt'; legendProps[$ENUM_LEGEND_PROPS.LEGEND_FONT_COLOR] = '#000000'; legendProps[$ENUM_LEGEND_PROPS.SHOW_LEGEND] = false; this.setDefaultPropertyValues(defaultPropertyValues); }, createGraphicModels() { const host = this; const dropZoneModel = this.zonesModel; const dropZoneAttribute = dropZoneModel.getDropZoneObjectsByName('Attribute'); const dropZoneMetric = dropZoneModel.getDropZoneObjectsByName('Metric'); const dropZoneColorBy = dropZoneModel.getDropZoneObjectsByName('Colorby'); const dropZoneSizeBy = dropZoneModel.getDropZoneObjectsByName('Size'); if (!dropZoneAttribute || dropZoneAttribute.length === 0 || !dropZoneMetric || dropZoneMetric.length === 0) { host.graphicModelsNA = true; throw new Error(); } host.hasColorBy = dropZoneColorBy && dropZoneColorBy.length > 0; host.hasSizeBy = dropZoneSizeBy && dropZoneSizeBy.length > 0; const rawData = this.dataInterface.getRawData( ENUM_RAW_DATA_FORMAT.ROWS_ADV, { hasSelection: true, hasTitleName: true, hasThreshold: true, additionalAttrIds: this.additionalAttrIds, colorByInfo: dropZoneModel.getColorByAttributes(), } ); host.maxValueSizeBy = -Infinity; host.minValueSizeBy = Infinity; return rawData.map((row) => { const { values, headers } = row; const graphicModel = new GraphicModel(); //In case of putting the same attribute/metric into the colorBy/sizeBy dropzone as Attribute/Metric, we will get only one header or value set here. const textColorBy = headers[1] ? headers[1].name : headers[0].name; const valueSizeBy = values[1] ? values[1].rv : values[0].rv; graphicModel.idValueMapping = row.idValueMapping; graphicModel.setCustomProperties({ text: headers[0].name, textColorBy: textColorBy, value: values[0].rv, valueSizeBy: valueSizeBy, paletteColor: host.getColorBy(row.colorInfo), }); graphicModel.setId(this.getSelectorHash(row.metricSelector)); graphicModel.setSelector(row.metricSelector, true); host.maxValueSizeBy = host.maxValueSizeBy < parseFloat(valueSizeBy) ? parseFloat(valueSizeBy) : host.maxValueSizeBy; host.minValueSizeBy = host.minValueSizeBy > parseFloat(valueSizeBy) ? parseFloat(valueSizeBy) : host.minValueSizeBy; return graphicModel; }); }, clearDomNode() { const canvasNode = this.canvasContainer; canvasNode.innerHTML = ""; }, plot(appliedProperties = null) { const host = this; host.clearDomNode(); const graphicModels = host.getGraphicModels(); if (graphicModels.length === 0) { return; } let properties; if (appliedProperties) { properties = adjustProperties(appliedProperties, host); } else { const savedProperties = JSON.parse(host.getProperty("unifiedProperty")); properties = adjustProperties(savedProperties, host); } // render part const { showAttribute, showMetric, attributeBackgroundColor, metricBackgroundColor, attributeFontFamily, attributeFontStyle, attributeFontColor, attributeFontSize, colorByFontFamily, colorByFontStyle, colorByFontColor, colorByFontSize, metricFontFamily, metricFontStyle, metricFontColor, metricFontSize, sizeByFontFamily, sizeByFontStyle, sizeByFontColor, sizeByFontSize, } = properties; const addStyle = ( element, fontFamily, fontColor, fontSize, fontStyle ) => { element.style.fontFamily = fontFamily; element.style.color = fontColor; element.style.fontSize = `${fontSize}pt`; const [isBold, isItalic, isUnderlined, isCrossedOut] = fontStyle; element.style.fontWeight = isBold ? "bold" : "normal"; element.style.fontStyle = isItalic ? "italic" : "normal"; const textDecorationOptions = []; if (isUnderlined) textDecorationOptions.push("underline"); if (isCrossedOut) textDecorationOptions.push("line-through"); const textDecoration = textDecorationOptions.join(" "); element.style.textDecoration = textDecoration; }; const drawSizeByItem = (sizeByContainer, sizeByItem, value) => { let percentage = parseFloat(value - host.minValueSizeBy) / (host.maxValueSizeBy - host.minValueSizeBy); percentage = (percentage + 0.3) / 1.3; const border = parseInt(getComputedStyle(sizeByContainer, null)['border']); const padding = parseInt(getComputedStyle(sizeByContainer, null)['padding']); const height = parseInt(getComputedStyle(sizeByContainer, null)['height']); const width = parseInt(getComputedStyle(sizeByContainer, null)['width']); sizeByItem.style.height = height * percentage + 'px'; sizeByItem.style.width = width * percentage + 'px'; sizeByItem.style.top = border + padding + height * (1 - percentage) / 2 + 'px'; } const gridColCnt = 4 - (host.hasSizeBy ? 0 : 1) - (host.hasColorBy ? 0 : 1); graphicModels.forEach((graphicModel) => { const container = document.createElement("div"); container.classList.add("container"); const attributeContainer = document.createElement("div"); attributeContainer.classList.add("attributeContainer"); attributeContainer.style.backgroundColor = attributeBackgroundColor; attributeContainer.style.width = (100.0 / gridColCnt) + '%'; const attributeContainerText = document.createTextNode( showAttribute ? graphicModel.text : "" ); addStyle( attributeContainer, attributeFontFamily, attributeFontColor, attributeFontSize, attributeFontStyle ); attributeContainer.appendChild(attributeContainerText); let colorByContainer; if (host.hasColorBy) { colorByContainer = document.createElement("div"); colorByContainer.classList.add("colorByContainer"); colorByContainer.style.backgroundColor = graphicModel.paletteColor; colorByContainer.style.width = (100.0 / gridColCnt) + '%'; const colorByContainerText = document.createTextNode( graphicModel.textColorBy ); addStyle( colorByContainer, colorByFontFamily, colorByFontColor, colorByFontSize, colorByFontStyle, ); colorByContainer.appendChild(colorByContainerText); } const metricContainer = document.createElement("div"); metricContainer.classList.add("metricContainer"); metricContainer.style.backgroundColor = metricBackgroundColor; metricContainer.style.width = (100.0 / gridColCnt) + '%'; const metricContainerText = document.createTextNode( showMetric ? graphicModel.value : "" ); addStyle( metricContainer, metricFontFamily, metricFontColor, metricFontSize, metricFontStyle ); metricContainer.appendChild(metricContainerText); let sizeByContainer; let sizeByItem; if (host.hasSizeBy) { sizeByContainer = document.createElement("div"); sizeByContainer.classList.add("sizeByContainer"); sizeByContainer.style.width = (100.0 / gridColCnt) + '%'; const sizeByContainerText = document.createTextNode( showMetric ? graphicModel.valueSizeBy : "" ); addStyle( sizeByContainer, sizeByFontFamily, sizeByFontColor, sizeByFontSize, sizeByFontStyle, ); sizeByItem = document.createElement("div"); sizeByItem.style.position = 'absolute'; sizeByItem.style.backgroundColor = '#CCCCCC'; sizeByItem.style.zIndex = -1; sizeByContainer.appendChild(sizeByItem); sizeByContainer.appendChild(sizeByContainerText); } container.appendChild(attributeContainer); if (host.hasColorBy) { container.appendChild(colorByContainer); } container.appendChild(metricContainer); if (host.hasSizeBy) { container.appendChild(sizeByContainer); } // Mount the canvas to the canvas container instead. host.canvasContainer.appendChild(container); if (host.hasSizeBy) { drawSizeByItem(sizeByContainer, sizeByItem, graphicModel.valueSizeBy); } }); }, } );