Forum Discussion
saviourofdp
8 years agoAdvocate I
Font Size Property Unit Issue in Custom Visual
Hello I have an issue with creating a custom visual in which I need to be able to set 2 different font sizes from the property pane. The issue is that the first font size control is a slider whi...
- 8 years ago
Hello saviourofdp
The fontSize is predefined Power BI capabilities name.
You might try to rename the second fontSize property to textSize, titleFontSize, secFontSize, secTitleFontSize.
Ignat Vilesov,
Software Engineer
Microsoft Power BI Custom Visuals
v-viig
7 years agoCommunity Champion
Not sure. Please share your code sample.
Ignat Vilesov,
Software Engineer
Microsoft Power BI Custom Visuals
tcd1nc
7 years agoFrequent Visitor
{
"dataRoles": [
{
"displayName": "Values",
"name": "values",
"kind": "Grouping"
}
],
"dataViewMappings": [
{
"conditions": [
{
"values": {
"max": 1
}
}
],
"categorical": {
"categories": {
"for": {
"in": "values"
},
"dataReductionAlgorithm": {
"top": {
"count": 100000
}
}
}
}
}
],
"objects": {
"defaults": {
"displayName": "Defaults",
"properties": {
"defaultId": {
"type": {
"integer": true
},
"displayName": "Default Item Index",
"description": "Select default item index"
}
}
},
"labels": {
"displayName": "Labels",
"properties": {
"vertical": {
"displayName": "Vertical",
"description": "If true, the radio buttons will be displayed vertically.",
"type": {
"bool": true
}
},
"backgroundColor": {
"type": {
"fill": {
"solid": {
"color": true
}
}
},
"displayName": "Background Color",
"description": "Select background color"
},
"fontSize": {
"type": {
"formatting": {
"fontSize": true
}
},
"displayName": "Font Size",
"description": "Select font size"
},
"fontColor": {
"type": {
"fill": {
"solid": {
"color": true
}
}
},
"displayName": "Font Color",
"description": "Select font color"
},
"fontFamily": {
"displayName": "Font family",
"description": "Select font family",
"type": {
"enumeration": [
{
"displayName": "Default",
"description": "helvetica, arial, sans-serif",
"value": "helvetica, arial, sans-serif"
},
{
"displayName": "Arial",
"value": "Arial"
},
{
"displayName": "Arial Black",
"value": "\"Arial Black\""
},
{
"displayName": "Arial Unicode MS",
"value": "\"Arial Unicode MS\""
},
{
"displayName": "Calibri",
"value": "Calibri"
},
{
"displayName": "Cambria",
"value": "Cambria"
},
{
"displayName": "Cambria Math",
"value": "\"Cambria Math\""
},
{
"displayName": "Candara",
"value": "Candara"
},
{
"displayName": "Comic Sans MS",
"value": "\"Comic Sans MS\""
},
{
"displayName": "Consolas",
"value": "Consolas"
},
{
"displayName": "Constantia",
"value": "Constantia"
},
{
"displayName": "Corbel",
"value": "Corbel"
},
{
"displayName": "Courier New",
"value": "\"Courier New\""
},
{
"displayName": "Georgia",
"value": "Georgia"
},
{
"displayName": "Lucida Sans Unicode",
"value": "\"Lucida Sans Unicode\""
},
{
"displayName": "Segoe (Bold)",
"value": "\"Segoe UI Bold\", wf_segoe-ui_bold, helvetica, arial, sans-serif"
},
{
"displayName": "Segoe UI",
"value": "\"Segoe UI\", wf_segoe-ui_normal, helvetica, arial, sans-serif"
},
{
"displayName": "Segoe UI Light",
"value": "\"Segoe UI Light\", wf_segoe-ui_bold, helvetica, arial, sans-serif"
},
{
"displayName": "Symbol",
"value": "Symbol"
},
{
"displayName": "Tahoma",
"value": "Tahoma"
},
{
"displayName": "Times New Roman",
"value": "\"Times New Roman\""
},
{
"displayName": "Trebuchet MS",
"value": "\"Trebuchet MS\""
},
{
"displayName": "Verdana",
"value": "Verdana"
},
{
"displayName": "Wingdings",
"value": "Wingdings"
}
]
}
},
"btnSize": {
"type": {
"integer": true
},
"displayName": "Button Size",
"description": "Select button size"
}
}
}
}
}visual.ts:
/*
* Power BI Visual CLI
*
* Copyright (c) Microsoft Corporation
* All rights reserved.
* MIT License
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the ""Software""), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
module powerbi.extensibility.visual {
"use strict";
interface FlexiSlicerViewModel {
vertical: boolean;
defaultId: number;
numberCats: number;
fontSize: number;
fontFamily: string;
fontColor: string;
backgroundColor: string;
radioBtnSize: number;
};
function visualTransform(options: VisualUpdateOptions, host: IVisualHost): FlexiSlicerViewModel {
let dataViews = options.dataViews;
let viewModel: FlexiSlicerViewModel = {
vertical: true,
defaultId: 0,
numberCats: 0,
fontSize: 12,
fontFamily: "Arial",
fontColor: "blue",
backgroundColor: "white",
radioBtnSize: 12
};
if (!dataViews
|| !dataViews[0]
|| !dataViews[0].categorical
|| !dataViews[0].categorical.categories
|| !dataViews[0].categorical.categories[0]
)
return viewModel;
let category = options.dataViews[0].categorical.categories[0];
var numCats = category.values.length;
let dvobjs = dataViews[0].metadata.objects;
try{
let style: FlexiSlicerViewModel = {
vertical: getValue<boolean>(dvobjs, 'labels', 'vertical', true),
defaultId: getValue<number>(dvobjs, 'defaults', 'defaultId', 0),
numberCats: numCats,
fontSize: getValue<number>(dvobjs, 'labels', 'fontSize', 12),
fontFamily: getValue<string>(dvobjs, 'labels', 'fontFamily', "Arial"),
fontColor: getFill(dataViews[0], 'labels', 'fontColor', "blue"),
backgroundColor: getFill(dataViews[0], 'labels', 'backgroundColor', "white"),
radioBtnSize: getValue<number>(dvobjs, 'labels', 'btnSize', 12)
};
return style;
}
catch(e){
return viewModel;
}
}
export class Visual implements IVisual {
private target: HTMLElement;
private flexiSlicerViewModel: FlexiSlicerViewModel;
//private numberCats: number;
private selectionManager: ISelectionManager;
private selectionIds: any = {};
private host: IVisualHost;
private isEventUpdate: boolean = false;
private lastSelectedValue: any;
constructor(options: VisualConstructorOptions) {
this.target = options.element;
this.host = options.host;
this.selectionManager = options.host.createSelectionManager();
}
public update(options: VisualUpdateOptions) {
this.flexiSlicerViewModel = visualTransform(options, this.host);
if (this.flexiSlicerViewModel && !this.isEventUpdate){
this.init(options);
//update if default item is set
if(this.flexiSlicerViewModel.defaultId > 0){
this.selectionManager.clear(); // Clean up previous filter before applying another one.
// Find the selectionId and select it
this.selectionManager.select(this.lastSelectedValue).then((ids: ISelectionId[]) => { });
// This call applys the previously selected selectionId
this.selectionManager.applySelectionFilter();
}
}
}
public init(options: VisualUpdateOptions) {
// Return if we don't have a category
if (!options ||
!options.dataViews ||
!options.dataViews[0] ||
!options.dataViews[0].categorical ||
!options.dataViews[0].categorical.categories ||
!options.dataViews[0].categorical.categories[0]) {
return;
}
let viewmodel = this.flexiSlicerViewModel;
// remove any children from previous renders
while (this.target.firstChild) {
this.target.removeChild(this.target.firstChild);
}
// clear out any previous selection ids
this.selectionIds = {};
// get the category data.
let category = options.dataViews[0].categorical.categories[0];
let values = category.values;
// build selection ids to be used by filtering capabilities later
var itemctr: number = 0;
let scroller = document.createElement("div");
scroller.className="container";
scroller.style.width = options.viewport.width.toString() +"px";
scroller.style.height = options.viewport.height.toString() +"px";
scroller.style.backgroundColor = viewmodel.backgroundColor;
values.forEach((item: number, index: number) => {
itemctr++;
// create an in-memory version of the selection id so it can be used in onclick event.
this.selectionIds[item] = this.host.createSelectionIdBuilder()
.withCategory(category, index)
.createSelectionId();
let value = item.toString();
let radio = document.createElement("input");
radio.type = "radio";
radio.value = value;
radio.name = "values";
radio.id = itemctr.toString();
radio.style.height= viewmodel.radioBtnSize.toString()+"px";// viewmodel.fontSize.toString() + "px";
radio.style.width= viewmodel.radioBtnSize.toString()+"px";//viewmodel.fontSize.toString() + "px";
//set default checked item
if(itemctr == viewmodel.defaultId ){
radio.checked = true;
this.lastSelectedValue = this.selectionIds[value];
}
radio.onclick = function (ev) {
this.isEventUpdate = true;
// This is checked in the update method. If true it won't re-render, this prevents an infinite loop
this.selectionManager.clear(); // Clean up previous filter before applying another one.
// select saved selectionid
this.selectionManager.select(this.selectionIds[value]).then((ids: ISelectionId[]) => { });
// This call applys the previously saved selectionId
this.selectionManager.applySelectionFilter();
}.bind(this);
let label = document.createElement("label");
label.innerHTML += value;
label.style.fontFamily= viewmodel.fontFamily;
label.style.color = viewmodel.fontColor;
label.style.fontSize = viewmodel.fontSize.toString() + "px";
label.htmlFor=itemctr.toString();
scroller.appendChild(radio);
scroller.appendChild(label);
if (viewmodel.vertical == true && itemctr < values.length ) {
scroller.appendChild(document.createElement("br"));
}
});
this.target.appendChild(scroller);
}
/**
* Enumerates through the objects defined in the capabilities and adds the properties to the format pane
*
* @function
* @param {EnumerateVisualObjectInstancesOptions} options - Map of defined objects
*/
//@logExceptions()
public enumerateObjectInstances(options: EnumerateVisualObjectInstancesOptions): VisualObjectInstanceEnumeration {
let objectName = options.objectName;
let objectEnumeration: VisualObjectInstance[] = [];
let viewModel = this.flexiSlicerViewModel;
switch (objectName) {
case 'defaults':
var id: number = 0;
if(viewModel.defaultId > viewModel.numberCats) id= 0; else id= viewModel.defaultId;
objectEnumeration.push({
objectName: objectName,
properties: {
defaultId: id
},
validValues: {
defaultId: {
numberRange: {
min: 0,
max: viewModel.numberCats
}
}
},
selector: null
});
break;
case 'labels':
objectEnumeration.push({
objectName: objectName,
properties: {
vertical: viewModel.vertical,
backgroundColor: viewModel.backgroundColor,
fontSize: viewModel.fontSize,
fontFamily: viewModel.fontFamily,
fontColor: viewModel.fontColor,
btnSize: viewModel.radioBtnSize
},
validValues: {
btnSize: {
numberRange: {
min: 1,
max: 40
}
}
},
selector: null
});
break;
};
this.isEventUpdate = false;
return objectEnumeration;
}
}
}- v-viig7 years agoCommunity Champion
Can you share full source code as a zip file?
Ignat Vilesov,
Software Engineer
Microsoft Power BI Custom Visuals