<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:taxo="http://purl.org/rss/1.0/modules/taxonomy/" version="2.0">
  <channel>
    <title>topic Re: Need help for defining properties in format visual pane. in Custom Visuals Development Discussion</title>
    <link>https://community.fabric.microsoft.com/t5/Custom-Visuals-Development/Need-help-for-defining-properties-in-format-visual-pane/m-p/4349688#M11990</link>
    <description>&lt;P&gt;Any Suggestions community ?&lt;/P&gt;</description>
    <pubDate>Fri, 03 Jan 2025 10:53:20 GMT</pubDate>
    <dc:creator>Sharma_Devesh</dc:creator>
    <dc:date>2025-01-03T10:53:20Z</dc:date>
    <item>
      <title>Need help for defining properties in format visual pane.</title>
      <link>https://community.fabric.microsoft.com/t5/Custom-Visuals-Development/Need-help-for-defining-properties-in-format-visual-pane/m-p/4343625#M11968</link>
      <description>&lt;P&gt;Hi,&amp;nbsp;&lt;/P&gt;&lt;P&gt;I've created a custom 3D Pie Chart using react + typescript.&lt;/P&gt;&lt;P&gt;I need to define property to&lt;/P&gt;&lt;P&gt;i) change color of each slice based on adoptive data&lt;/P&gt;&lt;P&gt;ii) font size &amp;amp; style of dataLabels&lt;/P&gt;&lt;P&gt;in format visual panel under Vizualizations pane.&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;P&gt;Please suggest a approach to take for same.&lt;/P&gt;&lt;P&gt;I'm attaching my source code snippets as well.&amp;nbsp;&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;P&gt;PieChart.tsx File:&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;LI-CODE lang="javascript"&gt;import * as React from "react";
import * as Highcharts from "highcharts";
import HighchartsReact from "highcharts-react-official";
import HC_more from "highcharts/highcharts-3d";
import powerbi from "powerbi-visuals-api";
import DataView = powerbi.DataView;
import IVisual = powerbi.extensibility.visual.IVisual;
import VisualConstructorOptions = powerbi.extensibility.visual.VisualConstructorOptions;
import VisualUpdateOptions = powerbi.extensibility.visual.VisualUpdateOptions;

// Initialize the 3D module
HC_more(Highcharts);

interface Props {
    dataView: DataView | undefined;
    chartData?: { name: string; y: number }[]; // Make chartData optional
}


interface State {
    chartOptions: Highcharts.Options;
}

export class CustomPieChart extends React.Component&amp;lt;Props, State&amp;gt; implements IVisual {
    private target: HTMLElement;
    private updateCount: number;

    constructor(props: Props) {
        super(props);
        this.updateCount = 0;
        const defaultData = [
            { name: "No Data", y: 100 }
        ];
        
        this.state = {
            chartOptions: this.getChartOptions(props.chartData || defaultData)
        };
    }

    public update(options: VisualUpdateOptions) {
        if (options.dataViews &amp;amp;&amp;amp; options.dataViews[0]) {
            const dataView = options.dataViews[0];
            const extractedData = this.extractDataFromDataView(dataView);
            const userDefinedColors = extractedData.map((_, index) =&amp;gt; {
                const dataPointObject = dataView.metadata.objects?.dataPoint;
                const fillProperty = dataPointObject &amp;amp;&amp;amp; dataPointObject[`fill_$[index]`];
                return typeof fillProperty === 'object' &amp;amp;&amp;amp; 'solid' in fillProperty ? fillProperty.solid.color : undefined;
                // return dataPointObject &amp;amp;&amp;amp; dataPointObject[`fill_${index}`]?.solid?.color;
            });
            const dynamicColors = extractedData.map((_, index) =&amp;gt;
                userDefinedColors[index] || this.state.chartOptions.colors?.[index]
            );
    
            const chartOptions = this.getChartOptions(extractedData);
            chartOptions.colors = dynamicColors; // Apply updated colors
    
            this.setState({ chartOptions: this.getChartOptions(extractedData) });
        }
    }

    componentDidMount() {
        if (this.props.dataView) {
            const extractedData = this.extractDataFromDataView(this.props.dataView);
            this.setState({ chartOptions: this.getChartOptions(extractedData) });
        }
    }

    componentDidUpdate(prevProps: Props) {
        if (prevProps.chartData !== this.props.chartData) {
            this.setState({ chartOptions: this.getChartOptions(this.props.chartData) });
        }
    }

    extractDataFromDataView(dataView: DataView): any[] {
        if (!dataView?.categorical?.categories?.[0]?.values || !dataView?.categorical?.values?.[0]?.values) {
            return [{ name: "No Data", y: 100 }];
        }

        try {
            const categorical = dataView.categorical;
            const categories = categorical.categories[0].values;
            const values = categorical.values[0].values;

            return categories.map((category, index) =&amp;gt; ({
                name: String(category),
                y: Number(values[index]) || 0
            }));
        } catch (error) {
            console.error("Error extracting data:", error);
            return [{ name: "Error", y: 100 }];
        }
    }

    getChartOptions(data: any[]): Highcharts.Options {
        return {
            colors: ['#AEDFF7', '#73C6F3', '#3498DB', '#2E86C1', '#1B4F72', '#154360'],
            // colors: ['#00a6e9', '#9bdafc'],
            chart: {
                type: 'pie',
                options3d: {
                    enabled: true,
                    alpha: 50,
                    beta: 0
                },
                height: 400,
                animation: true,
                backgroundColor: 'transparent'
            },
            title: {
                text: '3D Pie Chart',
                align: 'left',
                style: {
                    fontSize: '14px',
                    color: 'red'
                }
            },
            accessibility: {
                point: {
                    valueSuffix: '%'
                }
            },
            tooltip: {
                pointFormat: '{series.name}: &amp;lt;b&amp;gt;{point.percentage:.1f}%&amp;lt;/b&amp;gt;'
            },
            plotOptions: {
                pie: {
                    allowPointSelect: true,
                    cursor: 'pointer',
                    depth: 55,
                    dataLabels: {
                        enabled: true,
                        useHTML: true,
                        formatter: function () {
                            const point = this.point;
                            const color = point.color || 'black'; // Default to black if no color is defined
                            return `&amp;lt;span style="color:${color}; font-size:11px;"&amp;gt;${point.name}: ${point.percentage.toFixed(1)}%&amp;lt;/span&amp;gt;`;
                        },
                        style: {
                            fontSize: '11px',
                            textOutline: 'none' // Prevent text from having an outline
                        }
                    },
                    showInLegend: true
                }
            },
            legend: {
                enabled: true,
                itemStyle: {
                    fontSize: '11px'
                }
            },
            series: [{
                type: 'pie',
                name: 'Share',
                data: data
            }] as any,
            credits: {
                enabled: false
            }
        };
    }
    
    render() {
        return (
            &amp;lt;div style={{ 
                minHeight: "400px", 
                width: "100%", 
                display: "flex", 
                justifyContent: "center", 
                alignItems: "center" 
            }}&amp;gt;
                &amp;lt;HighchartsReact
                    highcharts={Highcharts}
                    options={this.state.chartOptions}
                    containerProps={{ style: { height: "100%", width: "100%" } }}
                /&amp;gt;
            &amp;lt;/div&amp;gt;
        );
    }
}

export default CustomPieChart;&lt;/LI-CODE&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;P&gt;&lt;BR /&gt;Visual.ts File:&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;LI-CODE lang="javascript"&gt;import * as React from "react";
import * as ReactDOM from "react-dom";
import DataView = powerbi.DataView;
import VisualConstructorOptions = powerbi.extensibility.visual.VisualConstructorOptions;
import VisualUpdateOptions = powerbi.extensibility.visual.VisualUpdateOptions;
import IVisual = powerbi.extensibility.visual.IVisual;
import ITooltipService = powerbi.extensibility.ITooltipService;
import IVisualHost = powerbi.extensibility.visual.IVisualHost;
import { VisualFormattingSettingsModel } from "./settings";
import customPieChart from "./PieChart";
import powerbi from "powerbi-visuals-api";
import "./../style/visual.less";

export class Visual implements IVisual {
    private chartData: Array&amp;lt;{ name: string; y: number }&amp;gt; = [{ name: "No Data", y: 100 }];
    private target: HTMLElement;
    private reactRoot: React.ReactElement;
    private dataView: DataView | undefined;
    private host: IVisualHost;
    private visualSettings: VisualFormattingSettingsModel;
    private tooltipService: ITooltipService;

    constructor(options: VisualConstructorOptions) {
        this.target = options.element;
        this.host = options.host;
        this.chartData = []
        this.tooltipService = this.host.tooltipService;

        // Initialize with empty chartData
        this.reactRoot = React.createElement(customPieChart, {
            dataView: undefined,
            chartData: [{ name: "No Data", y: 100 }]
        });
        ReactDOM.render(this.reactRoot, this.target);
    }

    public update(options: VisualUpdateOptions) {
        // Update the dataView with the new data
        this.dataView = options.dataViews?.[0];
        let extractedData = [{ name: "No Data", y: 100 }]; // Default data

        if (this.dataView &amp;amp;&amp;amp; this.dataView.categorical) {
            const categorical = this.dataView.categorical;
            const categories = categorical.categories?.[0]?.values;
            const values = categorical.values?.[0]?.values;

            if (categories &amp;amp;&amp;amp; values) {
                extractedData = categories.map((category, index) =&amp;gt; ({
                    name: String(category),
                    y: Number(values[index]) || 0
                }));
            }
        }

        // Re-render the component with the updated or default data
        this.reactRoot = React.createElement(customPieChart, {
            dataView: this.dataView,
            chartData: extractedData
        });
        ReactDOM.render(this.reactRoot, this.target);
    }

    public enumerateObjectInstances(options: powerbi.EnumerateVisualObjectInstancesOptions): powerbi.VisualObjectInstanceEnumeration {
        const enumeration: powerbi.VisualObjectInstance[] = [];
        if (options.objectName === "dataPoint") {
            this.chartData?.forEach((dataPoint: any, index: string | number) =&amp;gt; {
                enumeration.push({
                    objectName: options.objectName,
                    displayName: dataPoint.name,
                    selector: { id: dataPoint.name },
                    properties: {
                        fill: {
                            solid: {
                                color: this.chartData?.[index]?.color
                            }
                        }
                    },
                });
            });
        }
        return enumeration;
    }
}&lt;/LI-CODE&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;P&gt;&lt;BR /&gt;Capabilities.json File:&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;LI-CODE lang="javascript"&gt;{
  "dataRoles": [
    {
      "displayName": "Category",
      "name": "category",
      "kind": "Grouping"
    },
    {
      "displayName": "Values",
      "name": "values",
      "kind": "Measure"
    }
  ],
  "dataViewMappings": [
    {
      "categorical": {
        "categories": {
          "for": { "in": "category" },
          "dataReductionAlgorithm": { "top": {} }
        },
        "values": {
          "for": { "in": "values" }
        }
      }
    }
  ],
  
  "objects": {
    "general": {
      "displayName": "General",
      "properties": {
        "show": {
          "displayName": "Show",
          "type": { "bool": true }
        }
      }
    },
    "visuals": {
      "displayName": "Visuals",
      "properties": {
        "fill": {
          "displayName": "Fill",
          "type": { "fill": { "solid": { "color": true } } }
        }
      }
    },
    "dataPoint":{
      "displayName": "Data Point Colors",
      "properties": {
        "fill": {
          "type": {"fill": {"solid": {"color": true } } }
        }
      }
    },
    "colorSettings": {
      "displayName": "Colors",
      "properties": {
        "fill": {
          "displayName": "Fill Color",
          "type": { "fill": { "solid": { "color": true } } }
        }
      }
    },
    "sizeSettings": {
      "displayName": "Size",
      "properties": {
        "size": {
          "displayName": "Size",
          "type": { "numeric": true }
        }
      }
    }
  },
  "supportsHighlight": true,
  "supportsMultiVisualSelection": true,
  "tooltips": {
      "roles": ["category", "values"]
  },
  "privileges": []
}&lt;/LI-CODE&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;</description>
      <pubDate>Thu, 26 Dec 2024 12:19:49 GMT</pubDate>
      <guid>https://community.fabric.microsoft.com/t5/Custom-Visuals-Development/Need-help-for-defining-properties-in-format-visual-pane/m-p/4343625#M11968</guid>
      <dc:creator>Sharma_Devesh</dc:creator>
      <dc:date>2024-12-26T12:19:49Z</dc:date>
    </item>
    <item>
      <title>Re: Need help for defining properties in format visual pane.</title>
      <link>https://community.fabric.microsoft.com/t5/Custom-Visuals-Development/Need-help-for-defining-properties-in-format-visual-pane/m-p/4349688#M11990</link>
      <description>&lt;P&gt;Any Suggestions community ?&lt;/P&gt;</description>
      <pubDate>Fri, 03 Jan 2025 10:53:20 GMT</pubDate>
      <guid>https://community.fabric.microsoft.com/t5/Custom-Visuals-Development/Need-help-for-defining-properties-in-format-visual-pane/m-p/4349688#M11990</guid>
      <dc:creator>Sharma_Devesh</dc:creator>
      <dc:date>2025-01-03T10:53:20Z</dc:date>
    </item>
    <item>
      <title>Re: Need help for defining properties in format visual pane.</title>
      <link>https://community.fabric.microsoft.com/t5/Custom-Visuals-Development/Need-help-for-defining-properties-in-format-visual-pane/m-p/4350542#M11991</link>
      <description>&lt;P&gt;I think that you also need to configure settings&lt;/P&gt;&lt;P&gt;.ts for it to show under your visualizations pane. So the objects in capabilities also need to be correctly set-up in settings.ts&lt;/P&gt;</description>
      <pubDate>Sat, 04 Jan 2025 10:36:05 GMT</pubDate>
      <guid>https://community.fabric.microsoft.com/t5/Custom-Visuals-Development/Need-help-for-defining-properties-in-format-visual-pane/m-p/4350542#M11991</guid>
      <dc:creator>AnViL999</dc:creator>
      <dc:date>2025-01-04T10:36:05Z</dc:date>
    </item>
    <item>
      <title>Re: Need help for defining properties in format visual pane.</title>
      <link>https://community.fabric.microsoft.com/t5/Custom-Visuals-Development/Need-help-for-defining-properties-in-format-visual-pane/m-p/4350939#M11993</link>
      <description>&lt;P&gt;Hi, I've configured my settings.ts file as well. I'm able to get properties panel but the color change change is not reflected in UI.&amp;nbsp;&lt;BR /&gt;UI Looks like this:&amp;nbsp;&lt;BR /&gt;&lt;img /&gt;&lt;BR /&gt;&lt;BR /&gt;&lt;BR /&gt;here is my code for settings.ts file&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;LI-CODE lang="javascript"&gt;"use strict";

import { formattingSettings } from "powerbi-visuals-utils-formattingmodel";



class LabelsCardSetting extends formattingSettings.SimpleCard {
    name: string = "labels"; // same as capabilities object name
    displayName: string = "Labels";
    // selector is not needed and has been removed

    public fontFamily: formattingSettings.FontPicker = new formattingSettings.FontPicker({
        name: "fontFamily", // same as capabilities property name
        value: "Arial, sans-serif"
    });

    public fontSize: formattingSettings.NumUpDown = new formattingSettings.NumUpDown({
        name: "fontSize", // same as capabilities property name
        value: 11
    });

    public bold: formattingSettings.ToggleSwitch = new formattingSettings.ToggleSwitch({
        name: "bold", // same as capabilities property name
        value: false
    });

    public italic: formattingSettings.ToggleSwitch = new formattingSettings.ToggleSwitch({
        name: "italic", // same as capabilities property name
        value: false
    });

    public underline: formattingSettings.ToggleSwitch = new formattingSettings.ToggleSwitch({
        name: "underline", // same as capabilities property name
        value: false
    });

    public font: formattingSettings.FontControl = new formattingSettings.FontControl({
        name: "font",   // must be unique within the same object
        displayName: "Font",
        fontFamily: this.fontFamily,
        fontSize: this.fontSize,
        bold: this.bold,           //optional
        italic: this.italic,       //optional
        underline: this.underline  //optional
    });

    public slices: formattingSettings.Slice[] = [this.font];
}

// Slice color settings
class SlicesColorCardSetting extends formattingSettings.SimpleCard {
    name: string = "sliceColors";
    displayName: string = "Slice Colors";

    public dynamicSliceColors: formattingSettings.ColorPicker[] = [];
    private defaultColors: string[] = ['#AEDFF7', '#73C6F3', '#3498DB', '#2E86C1', '#1B4F72', '#154360'];

    // Dynamically generate ColorPicker settings based on the number of slices
    public updateSliceColors(sliceNames: string[], currentColors: Record&amp;lt;string, string&amp;gt; = {}) {
        // Create a lookup for existing colors
        const existingColors: Record&amp;lt;string, formattingSettings.ColorPicker&amp;gt; = {};
        for (const colorPicker of this.dynamicSliceColors) {
            existingColors[colorPicker.name] = {
                name: colorPicker.name,
                value: colorPicker.value,
            };
        }

        // Map slice names to dynamic slice colors
        const updatedSliceColors: formattingSettings.ColorPicker[] = [];
        for (let i = 0; i &amp;lt; sliceNames.length; i++) {
            const sliceName = sliceNames[i];
            const colorName = `sliceColor${i + 1}`;
            const defaultColor = this.defaultColors[i % this.defaultColors.length];

            updatedSliceColors.push(
                new formattingSettings.ColorPicker({
                    name: colorName,
                    displayName: `${sliceName} Color`,
                    value: typeof currentColors[colorName] === 'string' ? { value: currentColors[colorName] } : currentColors[colorName] || existingColors[colorName]?.value || defaultColor,
                })
            );
        }

        this.dynamicSliceColors = updatedSliceColors;
        this.slices = this.dynamicSliceColors;

        console.log("Updated Slice Colors:", this.dynamicSliceColors);
        console.log("Retained Colors:", existingColors);
        console.log("Dynamic Slice Colors After Update:", this.dynamicSliceColors);
    }

    public slices: formattingSettings.Slice[] = [];
}


export class VisualFormattingSettingsModel extends formattingSettings.Model {
    updateSliceColors(sliceNames: string[]) {
        throw new Error("Method not implemented.");
    }
    public labels: LabelsCardSetting = new LabelsCardSetting();
    public sliceColors: SlicesColorCardSetting = new SlicesColorCardSetting();
    public cards: formattingSettings.SimpleCard[] = [this.labels, this.sliceColors];
    static parse: any;

    // Bind font properties to DataLabels objects
    public bindToDataLabels(dataLabels: any) {
        dataLabels.fontFamily = this.labels.fontFamily.value;
        dataLabels.fontSize = this.labels.fontSize.value;
        dataLabels.bold = this.labels.bold.value;
        dataLabels.italic = this.labels.italic.value;
        dataLabels.underline = this.labels.underline.value;
        console.log(dataLabels);
    }

    // }
    public bindToSliceColors(sliceNames: string[], sliceColors: any[]) {
        const currentColors = sliceNames.reduce((acc, sliceName, index) =&amp;gt; {
            acc[`sliceColor${index + 1}`] = sliceColors[index];
            return acc;
        }, {} as Record&amp;lt;string, string&amp;gt;);

        this.sliceColors.updateSliceColors(sliceNames, currentColors);
        this.sliceColors.dynamicSliceColors.forEach((colorPicker, index) =&amp;gt; {
            sliceColors[index] = colorPicker.value.value; // Assuming colorPicker.value is an object with a 'value' property
        });

        // public getSelectedSliceColors(): any[] {
        //     return this.sliceColors.dynamicSliceColors.map(colorPicker =&amp;gt; colorPicker.value);
        // }
    }
}&lt;/LI-CODE&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;</description>
      <pubDate>Sun, 05 Jan 2025 12:06:32 GMT</pubDate>
      <guid>https://community.fabric.microsoft.com/t5/Custom-Visuals-Development/Need-help-for-defining-properties-in-format-visual-pane/m-p/4350939#M11993</guid>
      <dc:creator>Sharma_Devesh</dc:creator>
      <dc:date>2025-01-05T12:06:32Z</dc:date>
    </item>
  </channel>
</rss>

