Partilhar via


API de formatação no objeto (visualização)

de formatação no objeto permite que os usuários modifiquem rápida e facilmente o formato de elementos 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 formatação no objeto, consulte Formatação no objeto no Power BI Desktop.

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

Observação

  • Os elementos visuais que suportam a formatação no objeto precisam implementar o da API getFormattingModel, que está disponível na API versão 5.1.
  • 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 a subseleção ao Power BI. Forneça os estilos e atalhos de subseleção 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 onde 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 atualmente subselecionada como CustomVisualSubSelection.

Como implementar a API de formatação no objeto

Arquivo de recursos

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

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

IVisual interface

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 subselecionável é subselecionado. O método getSubSelectionStyles é fornecido com os elementos subselecionados atuais como uma matriz CustomVisualSubSelection e espera-se que retorne 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 modificar o estilo de um elemento.

getSubSelectionAtalhos

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 deve ser fornecido para que, quando um usuário subselecionar um elemento e o painel de formato estiver aberto, o painel role automaticamente para o cartão apropriado.

Existem 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 de desambiguação: O menu de desambiguação no objeto 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 o plano de fundo do visual. Para que o menu ambíguo apresente mais subseleções, o visual deve fornecer todas as subseleções através do método getSubSelectables.

getSubSelectables

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

Sub-Selection Edição direta de texto: Com a formatação On-Object, você pode clicar duas vezes no texto de um elemento subs-electable para editá-lo diretamente. Para fornecer o recurso de edição direta, você precisa fornecer um 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 a HTMLSubSelectionHelper você pode usar o atributo SubSelectableDirectEdit. (Consulte os atributos fornecidos pelo HTMLSubSelectionHelper)

A adição de uma edição direta para um ponto de dados específico (usando seletores) ainda não é suportada.

Interface FormattingId

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

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 um selectionId, use selectionId.getSelector(), esse seletor deve 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 subSelectionShortcuts e estilos para cada subselecionável.

Definir os objetos

Defina os objetos e declare que o visual está suportando a 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,

Crie os cartões de formatação

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

Configurações do cartão seletor de cores

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

Adicione um método ao 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. Este seletor é o usado ao implementar as APIs get do OnObject.

Configurações diretas do cartão de edição

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 on object 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, portanto, 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 seguinte interface para simplificar os exemplos:

Observação

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 Visual-cardName-card, onde 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 que você atribuiu a este 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 este exemplo, crie um enum para os nomes dos 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 o 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 formatação rola até o cartão seletor de cores e o abre
    • VisualShortcutType.Reset: adiciona um atalho de redefinição ao menu de contexto. É ativado 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 ecrã da interface do utilizador quando um utilizador clica com o botão direito do rato numa barra.

Ao alterar a cor:

Captura de tela da mudança de cor.

Atalhos da subsecção

Para implementar os atalhos e estilos de subSelection 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'
            }
        ];
    }

Este 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 no 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 interruptor de alternância para o cartão DirectEdit está desligado.
  • VisualShortcutType.Picker: adiciona uma opção no menu de contexto para escolher entre Direita e Esquerda, uma vez 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 de 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 formattingSettings.

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 objetos podem ser encontradas em (link a ser fornecido assim que a API for lançada) em on-object-formatting-api.d.ts
  • Recomendamos o uso do [on object utils], que inclui o [HTMLSubSelectionHelper](link a ser fornecido assim que a API for lançada)
  • 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 do objeto on usando o on object utils at (link a ser fornecido assim que a API for lançada)