Navegação no card

A maioria dos complementos baseados em cards é criada usando vários cards que representam diferentes "páginas" da interface do complemento. Para ter uma experiência do usuário eficaz, use uma navegação simples e natural entre os cards no seu complemento.

Originalmente nos complementos do Gmail, as transições entre diferentes cards da interface são processadas enviando e removendo cards de uma única pilha, com o card superior da pilha exibido pelo Gmail.

Navegação por cards na página inicial

Os complementos do Google Workspace apresentam páginas iniciais e cards não contextuais. Para acomodar cards contextuais e não contextuais, os complementos do Google Workspace têm uma pilha de cards interna para cada um. Quando um complemento é aberto em um host, o homepageTrigger correspondente é acionado para criar o primeiro card da página inicial na pilha (o card azul-escuro "página inicial" no diagrama abaixo). Se um homepageTrigger não for definido, um card padrão será criado, mostrado e enviado para a pilha não contextual. O primeiro card é um card raiz.

Seu complemento pode criar outros cards não contextuais e enviá-los para a pilha (os "cards enviados" azuis no diagrama) à medida que o usuário navega pelo complemento. A interface do complemento mostra o card superior na pilha. Portanto, ao inserir novos cards na pilha, a exibição muda. Ao remover cards da pilha, a exibição volta para os cards anteriores.

Se o complemento tiver um gatilho contextual definido, ele será acionado quando o usuário entrar nesse contexto. A função de gatilho cria o card contextual, mas a exibição da interface é atualizada com base no DisplayStyle do novo card:

  • Se o DisplayStyle for REPLACE (o padrão), o card contextual (o card laranja-escuro "contextual" no diagrama) vai substituir o card mostrado no momento. Isso inicia uma nova pilha de cards contextuais em cima da pilha não contextual, e esse card contextual é o card raiz da pilha contextual.
  • Se o DisplayStyle for PEEK, a interface vai criar um cabeçalho espiando que aparece na parte de baixo da barra lateral do complemento, sobrepondo o card atual. O cabeçalho de visualização mostra o título do novo card e oferece os controles de botão do usuário que permitem decidir se quer ver o novo card ou não. Se eles clicarem no botão Ver, o card vai substituir o atual (como descrito acima com REPLACE).

Você pode criar mais cards contextuais e enviá-los para a pilha (os "cards enviados" amarelos no diagrama). Atualizar a pilha de cards muda a interface do complemento para mostrar o card mais acima. Se o usuário sair de um contexto, os cards contextuais na pilha serão removidos, e a tela será atualizada para o card não contextual mais acima ou para a página inicial.

Se o usuário inserir um contexto para o qual o complemento não define um gatilho contextual, nenhum novo card será criado e o card atual vai continuar sendo exibido.

As ações de Navigation descritas abaixo só funcionam em cards do mesmo contexto. Por exemplo, popToRoot() em um card contextual só abre todos os outros cards contextuais e não afeta os cards da página inicial.

Por outro lado, o botão está sempre disponível para o usuário navegar dos cards contextuais para os não contextuais.

É possível criar transições entre cards adicionando ou removendo cards das pilhas. A classe Navigation fornece funções para enviar e remover cartas das pilhas. Para criar uma navegação por cards eficaz, configure os widgets para usar ações de navegação. É possível inserir ou remover vários cards simultaneamente, mas não é possível remover o card inicial da página inicial que é inserido na pilha quando o complemento é iniciado.

Para navegar até um novo card em resposta a uma interação do usuário com um widget, siga estas etapas:

  1. Crie um objeto Action e associe-o a uma função de callback que você definir.
  2. Chame a função de gerenciador de widgets apropriada do widget para definir o Action nele.
  3. Implemente a função de callback que realiza a navegação. Essa função recebe um objeto de evento de ação como argumento e precisa fazer o seguinte:
    1. Crie um objeto Navigation para definir a mudança de card. Um único objeto Navigation pode conter várias etapas de navegação, que são realizadas na ordem em que são adicionadas ao objeto.
    2. Crie um objeto ActionResponse usando a classe ActionResponseBuilder e o objeto Navigation.
    3. Retorna o ActionResponse criado.

Ao criar controles de navegação, você usa as seguintes funções de objeto Navigation:

Função Descrição
Navigation.pushCard(Card) Envia um card para a pilha atual. Isso exige a criação completa do card primeiro.
Navigation.popCard() Remove um card da parte de cima da pilha. Equivalente a clicar na seta para trás na linha de cabeçalho do complemento. Isso não remove os cards principais.
Navigation.popToRoot() Remove todos os cards da pilha, exceto o card raiz. Essencialmente, redefine a pilha de cards.
Navigation.popToNamedCard(String) Remove cards da pilha até chegar a um card com o nome especificado ou o card raiz da pilha. Você pode atribuir nomes aos cards usando a função CardBuilder.setName(String).
Navigation.updateCard(Card) Faz uma substituição no lugar do card atual, atualizando a exibição dele na UI.

Se uma interação ou evento do usuário resultar na renderização de cards no mesmo contexto, use os métodos Navigation.pushCard(), Navigation.popCard() e Navigation.updateCard() para substituir os cards atuais. Se uma interação ou evento do usuário precisar resultar na renderização de cards em um contexto diferente, use ActionResponseBuilder.setStateChanged() para forçar a reexecução do complemento nesses contextos.

Confira alguns exemplos de navegação:

  • Se uma interação ou evento mudar o estado do card atual (por exemplo, adicionar uma tarefa a uma lista de tarefas), use updateCard().
  • Se uma interação ou evento fornecer mais detalhes ou pedir ao usuário para realizar outra ação (por exemplo, clicar no título de um item para ver mais detalhes ou pressionar um botão para criar um novo evento do Google Agenda), use pushCard() para mostrar a nova página e permitir que o usuário saia dela usando o botão "Voltar".
  • Se uma interação ou evento atualizar o estado em um card anterior (por exemplo, atualizar o título de um item na visualização de detalhes), use algo como popCard(), popCard(), pushCard(previous) e pushCard(current) para atualizar o card anterior e o atual.

Atualizando cards

Com os complementos do Google Workspace, os usuários podem atualizar o card executando novamente a função de acionador do Apps Script registrada no manifesto. Os usuários acionam essa atualização usando um item de menu do complemento:

Barra lateral de complementos do Google Workspace

Essa ação é adicionada automaticamente aos cards gerados por funções de acionamento homepageTrigger ou contextualTrigger, conforme especificado no arquivo de manifesto do complemento (as "raízes" das pilhas de cards contextuais e não contextuais).

Retornar vários cards

Exemplo de cartão complementar

As funções de acionamento contextual ou da página inicial são usadas para criar e retornar um único objeto Card ou uma matriz de objetos Card que a interface do aplicativo mostra.

Se houver apenas um card, ele será adicionado à pilha não contextual ou contextual como o card raiz, e a interface do aplicativo host o mostrará.

Se a matriz retornada incluir mais de um objeto Card criado, o aplicativo host vai mostrar um novo card com uma lista do cabeçalho de cada card. Quando o usuário clica em um desses cabeçalhos, a interface mostra o card correspondente.

Quando o usuário seleciona um card na lista, ele é enviado para a pilha atual, e o aplicativo host o mostra. O botão retorna o usuário à lista de cabeçalhos de card.

Essa disposição "plana" de cards pode funcionar bem se o complemento não precisar de transições entre os cards criados. No entanto, na maioria dos casos, é melhor definir diretamente as transições de card e fazer com que a página inicial e as funções de acionamento contextual retornem um único objeto de card.

Exemplo

Confira um exemplo que mostra como construir vários cards com botões de navegação que pulam entre eles. Esses cards podem ser adicionados à pilha contextual ou não contextual ao enviar o card retornado por createNavigationCard() dentro ou fora de um contexto específico.

  /**
   *  Create the top-level card, with buttons leading to each of three
   *  'children' cards, as well as buttons to backtrack and return to the
   *  root card of the stack.
   *  @return {Card}
   */
  function createNavigationCard() {
    // Create a button set with actions to navigate to 3 different
    // 'children' cards.
    var buttonSet = CardService.newButtonSet();
    for(var i = 1; i <= 3; i++) {
      buttonSet.addButton(createToCardButton(i));
    }

    // Build the card with all the buttons (two rows)
    var card = CardService.newCardBuilder()
        .setHeader(CardService.newCardHeader().setTitle('Navigation'))
        .addSection(CardService.newCardSection()
            .addWidget(buttonSet)
            .addWidget(buildPreviousAndRootButtonSet()));
    return card.build();
  }

  /**
   *  Create a button that navigates to the specified child card.
   *  @return {TextButton}
   */
  function createToCardButton(id) {
    var action = CardService.newAction()
        .setFunctionName('gotoChildCard')
        .setParameters({'id': id.toString()});
    var button = CardService.newTextButton()
        .setText('Card ' + id)
        .setOnClickAction(action);
    return button;
  }

  /**
   *  Create a ButtonSet with two buttons: one that backtracks to the
   *  last card and another that returns to the original (root) card.
   *  @return {ButtonSet}
   */
  function buildPreviousAndRootButtonSet() {
    var previousButton = CardService.newTextButton()
        .setText('Back')
        .setOnClickAction(CardService.newAction()
            .setFunctionName('gotoPreviousCard'));
    var toRootButton = CardService.newTextButton()
        .setText('To Root')
        .setOnClickAction(CardService.newAction()
            .setFunctionName('gotoRootCard'));

    // Return a new ButtonSet containing these two buttons.
    return CardService.newButtonSet()
        .addButton(previousButton)
        .addButton(toRootButton);
  }

  /**
   *  Create a child card, with buttons leading to each of the other
   *  child cards, and then navigate to it.
   *  @param {Object} e object containing the id of the card to build.
   *  @return {ActionResponse}
   */
  function gotoChildCard(e) {
    var id = parseInt(e.parameters.id);  // Current card ID
    var id2 = (id==3) ? 1 : id + 1;      // 2nd card ID
    var id3 = (id==1) ? 3 : id - 1;      // 3rd card ID
    var title = 'CARD ' + id;

    // Create buttons that go to the other two child cards.
    var buttonSet = CardService.newButtonSet()
      .addButton(createToCardButton(id2))
      .addButton(createToCardButton(id3));

    // Build the child card.
    var card = CardService.newCardBuilder()
        .setHeader(CardService.newCardHeader().setTitle(title))
        .addSection(CardService.newCardSection()
            .addWidget(buttonSet)
            .addWidget(buildPreviousAndRootButtonSet()))
        .build();

    // Create a Navigation object to push the card onto the stack.
    // Return a built ActionResponse that uses the navigation object.
    var nav = CardService.newNavigation().pushCard(card);
    return CardService.newActionResponseBuilder()
        .setNavigation(nav)
        .build();
  }

  /**
   *  Pop a card from the stack.
   *  @return {ActionResponse}
   */
  function gotoPreviousCard() {
    var nav = CardService.newNavigation().popCard();
    return CardService.newActionResponseBuilder()
        .setNavigation(nav)
        .build();
  }

  /**
   *  Return to the initial add-on card.
   *  @return {ActionResponse}
   */
  function gotoRootCard() {
    var nav = CardService.newNavigation().popToRoot();
    return CardService.newActionResponseBuilder()
        .setNavigation(nav)
        .build();
  }