Compartilhar via


API de formatação no objeto (versão prévia)

formatação no objeto permite que os usuários modifiquem com rapidez e facilidade o formato dos visuais selecionando diretamente os elementos que desejam modificar. Quando um elemento é selecionado, o painel de formato navega automaticamente e expande a configuração de formatação específica para o elemento selecionado. Para obter mais informações sobre a formatação no objeto, consulte Formatação no objeto no Power BI Desktop.

Para adicionar essas funcionalidades ao visual, cada visual precisa fornecer uma opção de estilo de subseleção e um atalho para cada região subelecionável.

Nota

  • Os visuais que dão suporte à formatação no objeto precisam implementar a API getFormattingModel que está disponível na versão 5.1 da API.
  • Se você estiver usando powerbi-visuals-utils-formattingmodel, use pelo menos a versão 6.0.0.

Criar uma experiência no objeto

Use o serviço de subseleção quando o usuário selecionar um elemento subselecionável para enviar ao Power BI a subseleção. Forneça os estilos de subseleção e os atalhos usando a API de subseleção . O auxiliar de subseleção pode ser usado para simplificar o processo.

Modo de formato

O modo de formato é um novo modo em que o usuário pode ativar e desativar onObject formatação quando estiver no modo de criação. O visual é atualizado com o status do modo de formato nas opções de atualização. As opções de atualização também incluem a subSeleção subselecionária atualmente como CustomVisualSubSelection.

Como implementar a API de formatação no objeto

Arquivo de funcionalidades

No arquivo capabilites.json, adicione as seguintes propriedades para declarar que o visual dá suporte à formatação no objeto:

{
  "supportsOnObjectFormatting": true,
  "enablePointerEventsFormatMode": true,
}

Interface IVisual

O visual precisa implementar a interface VisualOnObjectFormatting como parte da interface IVisual.

VisualOnObjectFormatting contém três métodos:

getSubSelectionStyles

Cada visual é necessário para implementar um método getSubSelectionStyles, que é chamado quando um elemento subelecionável é subselecionado. O método getSubSelectionStyles é fornecido com os elementos subselecionados atuais como uma matriz de CustomVisualSubSelection e deve retornar um objeto SubSelectionStyles ou undefined.

Há três categorias de estilos de subseleção que abrangem a maioria dos cenários:

  • Texto
  • Texto numérico
  • Forma

Cada objeto SubSelectionStyles fornece uma experiência diferente para o usuário para modificar o estilo de um elemento.

getSubSelectionShortcuts

Para fornecer mais opções para o usuário, o visual deve implementar o método getSubSelectionShortcuts. Esse método retorna VisualSubSelectionShortcuts ou undefined. Além disso, se SubSelectionShortcuts forem fornecidos, um VisualNavigateSubSelectionShortcut também deverá ser fornecido para que, quando um usuário subseleciona um elemento e o painel de formato estiver aberto, o painel role automaticamente para o cartão apropriado.

Há vários atalhos de subseleção para modificar o estado visual. Cada um define um item de menu no menu de contexto com o rótulo apropriado.

Sub-Selection Menu Desambiguação: o menu de desambiguação On-Object fornece um método para os usuários selecionarem a subseleção desejada quando não estiver claro qual elemento visual está sendo subselecionado. Isso geralmente acontece quando o usuário subseleciona a tela de fundo do visual. Para que o menu disâmbguo apresente mais subseleções, o visual deve fornecer todas as subseleções por meio do método getSubSelectables.

getSubSelectables

Para fornecer subeleções ao menu de desambiguação, o visual precisa implementar o método getSubSelectables. Esse método recebe um argumento filterType opcional, do tipo SubSelectionStylesType e retorna uma matriz de CustomVisualSubSelection ou undefined. Se o HTMLSubSelectionHelper estiver sendo utilizado para criar uma subseleção, o método HTMLSubSelectionHelper.getSubSelectables() pode ser usado para coletar elementos subselecionáveis do DOM.

Sub-Selection Edição direta de texto: com formatação on-object, você pode clicar duas vezes no texto de um elemento subelegível para editá-lo diretamente. Para fornecer recursos de edição direta, você precisa fornecer uma RectangleSubSelectionOutline com a propriedade cVDirectEdit apropriada preenchida com um objeto SubSelectableDirectEdit. A estrutura de tópicos pode ser fornecida como uma estrutura de tópicos personalizada ou, se você estiver usando o HTMLSubSelectionHelper poderá usar o atributo SubSelectableDirectEdit. (Consulte os atributos fornecidos pelo HTMLSubSelectionHelper)

Ainda não há suporte para a adição de uma edição direta para um ponto de dados específico (usando seletores).

Interface FormattingId

A interface a seguir é usada para fazer referência aos atalhos e estilos subSelection.

interface FormattingId {
            objectName: string;
            propertyName: string;
            selector?: powerbi.data.Selector;
        }
  • objectName: o nome do objeto conforme declarado no capabilities.json.
  • propertyName: o nome da propriedade de um objeto conforme declarado no capabilities.json.
  • seletor: se o ponto de dados tiver uma selectionId, use selectionId.getSelector(), esse seletor deverá ser o mesmo fornecido para a fatia do modelo de formatação.

Exemplos

Neste exemplo, criamos um visual personalizado que tem dois objetos, colorSelector e directEdit. Usamos o HTMLSubSelectionHelper do onobjectFormatting utils para lidar com a maior parte do trabalho de subSeleção. Para obter mais informações, consulte utils no objeto.

Primeiro, criamos cartões para o painel de formatação e fornecemos estilos de subSelectionShortcuts e para cada subeleção.

Definir os objetos

Defina os objetos e declare que o visual dá suporte à Formatação OnObject no capabilities.json:

"objects": {
      "directEdit": {
      "properties": {
        "show": {
          "displayName": "Show",
          "type": {
            "bool": true
          }
        },
        "textProperty": {
          "displayName": "Text",
          "type": {
            "text": true
          }
        },
        "fontFamily": {
          "type": {
            "formatting": {
              "fontFamily": true
            }
          }
        },
        "fontSize": {
          "type": {
            "formatting": {
              "fontSize": true
            }
          }
        },
        "bold": {
          "type": {
            "bool": true
          }
        },
        "italic": {
          "type": {
            "bool": true
          }
        },
        "underline": {
          "type": {
            "bool": true
          }
        },
        "fontColor": {
          "displayName": "Font Color",
          "type": {
            "fill": {
              "solid": {
                "color": true
              }
            }
          }
        },
        "background": {
          "displayName": "Background",
          "type": {
            "fill": {
              "solid": {
                "color": true
              }
            }
          }
        },
        "position": {
          "displayName": "Position",
          "type": {
            "enumeration": [
              { "displayName": "Left", "value": "Left" }, { "displayName": "Right", "value": "Right" }
            ]
          }
        }
      }
    },
    "colorSelector": {
      "displayName": "Data Colors",
      "properties": {
        "fill": {
          "displayName": "Color",
          "type": {
            "fill": {
              "solid": {
                "color": true
              }
            }
          }
        }
      }
    },
   },
  "supportsOnObjectFormatting": true,
  "enablePointerEventsFormatMode": true,

Criar os cartões de formatação

Crie seus cartões de formatação usando os utils formattingModel.

Configurações do cartão seletor de cores

class ColorSelectorCardSettings extends Card {
    name: string = "colorSelector";
    displayName: string = "Data Colors";
    slices = [];
}

Adicione um método à formattingSetting para que possamos preencher as fatias dinamicamente para o objeto colorSelector (nossos pontos de dados).

populateColorSelector(dataPoints: BarChartDataPoint[]) {
        let slices: formattingSettings.ColorPicker[] = this.colorSelector.slices;
        if (dataPoints) {
            dataPoints.forEach(dataPoint => {
                slices.push(new formattingSettings.ColorPicker({
                    name: "fill",
                    displayName: dataPoint.category,
                    value: { value: dataPoint.color },
                    selector: dataPoint.selectionId.getSelector(),
                }));
            });
        }
    }

Passamos o seletor do ponto de dados específico no campo seletor. Esse seletor é o usado ao implementar as APIs get do OnObject.

Configurações de cartão de edição direta

class DirectEditSettings extends Card {
    displayName = 'Direct Edit';
    name = 'directEdit';
    private minFontSize: number = 8;
    private defaultFontSize: number = 11;
    show = new formattingSettings.ToggleSwitch({
        name: "show",
        displayName: undefined,
        value: true,
    });
    topLevelSlice = this.show;
    textProperty = new formattingSettings.TextInput({
        displayName: "Text Property",
        name: "textProperty",
        value: "What is your quest?",
        placeholder: ""
    });
    position = new formattingSettings.ItemDropdown({
        name: 'position',
        items: [{ displayName: 'Left', value: 'Left' }, { displayName: 'Right', value: 'Right' }],
        value: { displayName: 'Right', value: 'Right' }
    });
    font = new formattingSettings.FontControl({
        name: "font",
        displayName: 'Font',
        fontFamily: new formattingSettings.FontPicker({
            name: "fontFamily",
            displayName: "Font Family",
            value: "Segoe UI, wf_segoe-ui_normal, helvetica, arial, sans-serif"
        }),
        fontSize: new formattingSettings.NumUpDown({
            name: "fontSize",
            displayName: "Font Size",
            value: this.defaultFontSize,
            options: {
                minValue: {
                    type: powerbi.visuals.ValidatorType.Min,
                    value: this.minFontSize,
                }
            }
        }),
        bold: new formattingSettings.ToggleSwitch({
            name: 'bold',
            displayName: "Font Size",
            value: true
        }),
        italic: new formattingSettings.ToggleSwitch({
            name: 'italic',
            displayName: "Font Size",
            value: true
        }),
        underline: new formattingSettings.ToggleSwitch({
            name: 'underline',
            displayName: "Font Size",
            value: true
        })
    });
    fontColor = new formattingSettings.ColorPicker({
        name: "fontColor",
        displayName: "Color",
        value: { value: "#000000" }
    });
    background = new formattingSettings.ColorPicker({
        name: "background",
        displayName: "Color",
        value: { value: "#FFFFFF" }
    });
    slices = [this.show, this.textProperty, this.font, this.fontColor, this.background, this.position];
}

Usar atributos auxiliares de subseleção

Adicione os atributos HTMLSubSelectionHelper aos nossos objetos. Para ver quais atributos o HTMLSubSelectionHelper fornece, verifique a documentação sobre objeto utils.

  • Para o atributo directEdit:

    import {
       HtmlSubSelectableClass, HtmlSubSelectionHelper, SubSelectableDirectEdit as SubSelectableDirectEditAttr,
       SubSelectableDisplayNameAttribute, SubSelectableObjectNameAttribute, SubSelectableTypeAttribute 
    } from 'powerbi-visuals-utils-onobjectutils';
    
    const DirectEdit: powerbi.visuals.SubSelectableDirectEdit = {
        reference: {
            objectName: 'directEdit',
            propertyName: 'textProperty'
        },
        style: SubSelectableDirectEditStyle.Outline,
    };
    private visualDirectEditSubSelection = JSON.stringify(DirectEdit);
    
    this.directEditElement
                .classed('direct-edit', true)
                .classed('hidden', !this.formattingSettings.directEditSettings.show.value)
                .classed(HtmlSubSelectableClass, options.formatMode && this.formattingSettings.directEditSettings.show.value)
                .attr(SubSelectableObjectNameAttribute, 'directEdit')
                .attr(SubSelectableDisplayNameAttribute, 'Direct Edit')
                .attr(SubSelectableDirectEditAttr, this.visualDirectEditSubSelection)
    

    O HTMLSubSelectionHelper usa o atributo SubSelectableDirectEditAttr para fornecer a referência directEdit da estrutura de tópicos directEdit, de modo que uma edição direta é iniciada quando um usuário clica duas vezes no elemento.

    Captura de tela mostrando como o auxiliar de subseleção funciona.

  • Para o colorSelector:

    barSelectionMerged
              .attr(SubSelectableObjectNameAttribute, 'colorSelector')
              .attr(SubSelectableDisplayNameAttribute, (dataPoint: BarChartDataPoint) => this.formattingSettings.colorSelector.slices[dataPoint.index].displayName)
              .attr(SubSelectableTypeAttribute, powerbi.visuals.SubSelectionStylesType.Shape)
              .classed(HtmlSubSelectableClass, options.formatMode)
    
    

Definir referências

Defina a interface a seguir para simplificar os exemplos:

Nota

O cardUid fornecido deve ser o mesmo fornecido para a API getFormattingModel. Por exemplo, se você estiver usando powerbi-visuals-utils-formattingmodel, forneça o cardUid como cartão Visual-cardName, em que o cardName é o nome atribuído a esse cartão nas configurações do modelo de formatação. Caso contrário, forneça-o como o Visual-cardUid atribuído a esse cartão.

interface References {
    cardUid?: string;
    groupUid?: string;
    fill?: FormattingId;
    font?: FormattingId;
    fontColor?: FormattingId;
    show?: FormattingId;
    fontFamily?: FormattingId;
    bold?: FormattingId;
    italic?: FormattingId;
    underline?: FormattingId;
    fontSize?: FormattingId;
    position?: FormattingId;
    textProperty?: FormattingId;
}

Para a finalidade deste exemplo, crie uma enumeração para os nomes de objetos:

const enum BarChartObjectNames {
    ColorSelector = 'colorSelector',
    DirectEdit = 'directEdit'
}
  • Referências para o objeto directEdit:
const directEditReferences: References = {
    cardUid: 'Visual-directEdit-card',
    groupUid: 'directEdit-group',
    fontFamily: {
        objectName: BarChartObjectNames.DirectEdit,
        propertyName: 'fontFamily'
    },
    bold: {
        objectName: BarChartObjectNames.DirectEdit,
        propertyName: 'bold'
    },
    italic: {
        objectName: BarChartObjectNames.DirectEdit,
        propertyName: 'italic'
    },
    underline: {
        objectName: BarChartObjectNames.DirectEdit,
        propertyName: 'underline'
    },
    fontSize: {
        objectName: BarChartObjectNames.DirectEdit,
        propertyName: 'fontSize'
    },
    fontColor: {
        objectName: BarChartObjectNames.DirectEdit,
        propertyName: 'fontColor'
    },
    show: {
        objectName: BarChartObjectNames.DirectEdit,
        propertyName: 'show'
    },
    position: {
        objectName: BarChartObjectNames.DirectEdit,
        propertyName: 'position'
    },
    textProperty: {
        objectName: BarChartObjectNames.DirectEdit,
        propertyName: 'textProperty'
    }
};
  • Para colorSelector:
const colorSelectorReferences: References = {
    cardUid: 'Visual-colorSelector-card',
    groupUid: 'colorSelector-group',
    fill: {
        objectName: BarChartObjectNames.ColorSelector,
        propertyName: 'fill'
    }
};

Implementar APIs

Agora, vamos implementar as APIs get para a formatação onObject e fornecê-las no visualOnObjectFormatting:

  1. No código do construtor, forneça os métodos get no visualOnObjectFormatting:

    public visualOnObjectFormatting: powerbi.extensibility.visual.VisualOnObjectFormatting;
    constructor(options: VisualConstructorOptions) {
            this.subSelectionHelper = HtmlSubSelectionHelper.createHtmlSubselectionHelper({
                     hostElement: options.element,
                     subSelectionService: options.host.subSelectionService,
                     selectionIdCallback: (e) => this.selectionIdCallback(e),
                });
    
     this.visualOnObjectFormatting = {
                    getSubSelectionStyles: (subSelections) => this.getSubSelectionStyles(subSelections),
                    getSubSelectionShortcuts: (subSelections, filter) => this.getSubSelectionShortcuts(subSelections, filter),
                    getSubSelectables: (filter) => this. getSubSelectables(filter)
                }
       }
    
    private getSubSelectionStyles(subSelections: CustomVisualSubSelection[]): powerbi.visuals.SubSelectionStyles | undefined {
            const visualObject = subSelections[0]?.customVisualObjects[0];
            if (visualObject) {
                switch (visualObject.objectName) {
                    case BarChartObjectNames.ColorSelector:
                        return this.getColorSelectorStyles(subSelections);
                     case BarChartObjectNames.DirectEdit:
                        return this.getDirectEditStyles();
                }
            }
        }
    
    private getSubSelectionShortcuts(subSelections: CustomVisualSubSelection[], filter: SubSelectionShortcutsKey | undefined):    VisualSubSelectionShortcuts | undefined {
            const visualObject = subSelections[0]?.  customVisualObjects[0];
            if (visualObject) {
                switch (visualObject.objectName) {
                    case BarChartObjectNames.ColorSelector:
                        return this.getColorSelectorShortcuts(subSelections);
                    case BarChartObjectNames.DirectEdit:
                        return this.getDirectEditShortcuts();
                }
            }
        }
    
  2. Implemente os atalhos e o estilo getSubSelection para o colorSelector:

    private getColorSelectorShortcuts(subSelections:  CustomVisualSubSelection[]): VisualSubSelectionShortcuts   {
            const selector = subSelections[0].customVisualObjects[0].selectionId?.getSelector();
            return [
                {
                    type: VisualShortcutType.Reset,
                    relatedResetFormattingIds: [{
                        ...colorSelectorReferences.fill,
                        selector
                    }],
                },
                {
                    type: VisualShortcutType.Navigate,
                    destinationInfo: { cardUid: colorSelectorReferences.cardUid },
                    label: 'Color'
                }
            ];
        }
    

    O atalho acima retorna um item de menu relevante no menu de contexto e adiciona as seguintes funcionalidades:

    • VisualShortcutType.Navigate: quando um usuário seleciona em uma das barras (ponto de dados) e o painel de formatação está aberto, o painel de formato rola até o cartão seletor de cores e o abre
    • VisualShortcutType.Reset: adiciona um atalho de redefinição ao menu de contexto. Ele será habilitado se a cor de preenchimento tiver sido alterada.
    private getColorSelectorStyles(subSelections: CustomVisualSubSelection[]): SubSelectionStyles {
            const selector = subSelections[0].customVisualObjects[0].selectionId?.getSelector();
            return {
                type: SubSelectionStylesType.Shape,
                fill: {
                    label: 'Fill',
                    reference: {
                        ...colorSelectorReferences.fill,
                     selector
                    },
                },
            };
        }
    

Quando um usuário clica com o botão direito do mouse em uma barra, o seguinte aparece:

Captura de tela da interface do usuário quando um usuário clica com o botão direito do mouse em uma barra.

Ao alterar a cor:

Captura de tela da alteração da cor.

Atalhos de subseção

Para implementar os atalhos e estilos de subSeleção para o directEdit:

private getDirectEditShortcuts(): VisualSubSelectionShortcuts {
        return [
            {
                type: VisualShortcutType.Reset,
                relatedResetFormattingIds: [
                    directEditReferences.bold,
                    directEditReferences.fontFamily,
                    directEditReferences.fontSize,
                    directEditReferences.italic,
                    directEditReferences.underline,
                    directEditReferences.fontColor,
                    directEditReferences.textProperty
                ]
            },
            {
                type: VisualShortcutType.Toggle,
                relatedToggledFormattingIds: [{
                    ...directEditReferences.show,
                }],
                ...directEditReferences.show,
                disabledLabel: 'Delete',
            },
            {
                type: VisualShortcutType.Picker,
                ...directEditReferences.position,
                label: 'Position'
            },
            {
                type: VisualShortcutType.Navigate,
                destinationInfo: { cardUid: directEditReferences.cardUid },
                label: 'Direct edit'
            }
        ];
    }

Esse atalho adiciona um item de menu relevante no menu de contexto e adiciona as seguintes funcionalidades:

  • VisualShortcutType.Reset: adiciona uma redefinição ao item padrão ao menu de contexto, quando uma das propriedades fornecidas na matriz relatedResetFormattingIds é alterada.
  • VisualShortcutType.Toggle: adiciona opções de exclusão ao menu de contexto. Quando clicado, o botão de alternância para o cartão directEdit é desativado.
  • VisualShortcutType.Picker: adiciona uma opção no menu de contexto para escolher entre Direita e Esquerda, já que adicionamos a fatia de posição no cartão de formatação para o directEdit.
  • VisualShortcutType.Navigate: quando o painel de formato é aberto e o usuário seleciona o elemento directEdit, o painel de formato rola e abre o cartão directEdit.
private getDirectEditStyles(): SubSelectionStyles {
        return {
            type: powerbi.visuals.SubSelectionStylesType.Text,
            fontFamily: {
                reference: {
                    ...directEditReferences.fontFamily
                },
                label: 'font family'
            },
            bold: {
                reference: {
                    ...directEditReferences.bold
                },
                label: 'bold'
            },
            italic: {
                reference: {
                    ...directEditReferences.italic
                },
                label: 'italic'
            },
            underline: {
                reference: {
                    ...directEditReferences.underline
                },
                label: 'underline'
            },
            fontSize: {
                reference: {
                    ...directEditReferences.fontSize
                },
                label: 'font size'
            },
            fontColor: {
                reference: {
                    ...directEditReferences.fontColor
                },
                label: 'font color'
            },
            background: {
                reference: {
                    objectName: 'directEdit',
                    propertyName: 'background'
                },
                label: 'background'
            }
        }
    }

Fornecemos as propriedades relevantes à medida que as adicionamos nas formatttingSettings.

A imagem a seguir ilustra a aparência da interface do usuário ao clicar com o botão direito do mouse no elemento directEdit:

Captura de tela da interface de edição direta.

Localização

O visual deve manipular a localização e fornecer cadeias de caracteres localizadas.

Recursos do GitHub

  • Todas as interfaces de formatação de objeto podem ser encontradas (link a ser fornecido depois que a API é lançada) no on-object-formatting-api.d.ts
  • É recomendável usar o [on object utils], que incluem o [HTMLSubSelectionHelper](link a ser fornecido depois que a API for liberada)
  • Você pode encontrar um exemplo de um visual personalizado SampleBarChart que usa a API versão 5.8.0 e implementa o suporte para a formatação no objeto usando os utils de objeto no (link a ser fornecido depois que a API é liberada)