Navegação do card

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

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

Navegação do card da página inicial

Os complementos do Google Workspace apresentam páginas iniciais e cards não contextuais. Para acomodar cartões contextuais e não contextuais, os complementos do Google Workspace têm uma pilha de cartões 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 "homepage" azul-escuro no diagrama abaixo). Se um homepageTrigger não estiver definido, um cartão padrão será criado, exibido e enviado para a pilha não contextual. O primeiro é um cartão raiz.

Seu complemento pode criar outros cards não contextuais e enviá-los para a pilha (os "cards enviados" em azul no diagrama) conforme o usuário navega pelo complemento. A IU dos complementos exibe o cartão superior na pilha, portanto, o envio de novos cartões para a pilha altera a exibição, e remover os cards da pilha retorna a exibição para os cards anteriores.

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

  • Se o DisplayStyle for REPLACE (o padrão), o card de contexto (o cartão "contexto" laranja escuro no diagrama) vai substituir o card exibido. Isso inicia efetivamente uma nova pilha de cards contextuais sobre a pilha de cards não contextuais. Esse card é raiz da pilha de contexto.
  • Se o DisplayStyle for PEEK, a IU vai criar um cabeçalho de exibição na parte inferior da barra lateral do complemento, sobrepondo o card atual. O cabeçalho da exibição mostra o título do novo cartão e fornece os controles do botão de usuário que permitem decidir se o novo card vai ser exibido ou não. Se ele clicar no botão Ver, o cartão substituirá o cartão atual (conforme descrito acima por REPLACE).

É possível criar outros cards contextuais e enviá-los para a pilha (os "cards enviados" amarelos no diagrama). A atualização da pilha de cartões altera a IU do complemento para exibir o cartão mais próximo. Se o usuário sair de um contexto, os cards contextuais na pilha serão removidos e a exibição será atualizada no card ou na página inicial não contextual principal.

Se o usuário entrar em um contexto em que seu complemento não define um acionador contextual, nenhum cartão novo será criado e o cartão atual permanecerá exibido.

As ações Navigation descritas abaixo atuam apenas em cards do mesmo contexto. Por exemplo, popToRoot() em um card contextual mostra apenas 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 cartões adicionando ou removendo cards das pilhas de cartões. A classe Navigation fornece funções para enviar e retirar cards das pilhas. Para criar uma navegação eficaz com cards, configure os widgets para usar ações de navegação. Você pode enviar ou abrir vários cartões simultaneamente, mas não é possível remover o cartão inicial da página inicial que é enviado pela primeira vez para a 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 o associe a uma função de callback definida por você.
  2. Chame a função de gerenciador de widget apropriada para definir a Action nesse widget.
  3. Implemente a função de callback que conduz 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 no cartão. Um único objeto Navigation pode conter várias etapas de navegação, que são conduzidas na ordem em que são adicionadas ao objeto.
    2. Crie um objeto ActionResponse usando a classe ActionResponseBuilder e o objeto Navigation.
    3. Retorne o ActionResponse criado.

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

Função Descrição
Navigation.pushCard(Card) Envia um cartão para a pilha atual. Isso requer a criação completa do cartão.
Navigation.popCard() Remove um cartão da parte de cima da pilha. Equivalente a clicar na seta para voltar na linha do cabeçalho do complemento. Isso não remove os cartões raiz.
Navigation.popToRoot() Remove todos os cartões da pilha, exceto o cartão raiz. Basicamente, redefine essa pilha do cartão.
Navigation.popToNamedCard(String) Destaca os cartões da pilha até atingirem um cartão com o nome informado ou o cartão raiz da pilha. É possível atribuir nomes a cards usando a função CardBuilder.setName(String).
Navigation.updateCard(Card) Faz uma substituição no lugar do cartão atual, atualizando a exibição dele na IU.

Se uma interação do usuário ou um evento precisar resultar em uma nova 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 do usuário ou um evento resultar em uma nova renderização de cards em um contexto diferente, use ActionResponseBuilder.setStateChanged() para forçar a nova execução do complemento nesses contextos.

Veja a seguir exemplos de navegação:

  • Se uma interação ou um evento mudar o estado do card atual (por exemplo, adicionar uma tarefa a uma lista de tarefas), use updateCard().
  • Se uma interação ou um evento fornecer mais detalhes ou solicitar ao usuário mais ações (por exemplo, clicar no título de um item para ver mais detalhes ou pressionar um botão para criar um novo evento do Agenda), use pushCard() para mostrar a nova página enquanto permite que o usuário saia da nova página usando o botão "Voltar".
  • Se uma interação ou um evento for atualizado no estado de um card anterior (por exemplo, atualizando o título de um item com a visualização detalhada), use algo como popCard(), popCard(), pushCard(previous) e pushCard(current) para atualizar o cartão anterior e o atual.

Atualizando cards

Com os complementos do Google Workspace, os usuários podem atualizar seu cartão executando novamente a função de gatilho do Apps Script registrada no seu manifesto. Os usuários acionam essa atualização em um item de menu complementar:

Barra lateral de complementos do Google Workspace Barra lateral de complementos do Google Workspace





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

Como retornar vários cartões

Exemplo de card de complemento

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

Se houver apenas um cartão, ele vai ser adicionado à pilha não contextual ou contextual, como o cartão raiz e a IU do aplicativo host o exibe.

Se a matriz retornada incluir mais de um objeto Card criado, o aplicativo host exibirá um novo cartão, que contém uma lista de cabeçalhos de cada cartão. Quando o usuário clica em qualquer um desses cabeçalhos, a IU mostra o cartão correspondente.

Quando o usuário seleciona um cartão da lista, esse cartão é enviado para a pilha atual, e o aplicativo host o exibe. O botão retorna o usuário à lista de cabeçalhos do cartão.

Essa organização de cards "simples" pode funcionar bem se o complemento não precisar de transições entre cartões criados por você. Na maioria dos casos, no entanto, é uma prática recomendada definir diretamente as transições de cartão e fazer com que a página inicial e as funções de acionadores contextuais retornem um único objeto de cartão.

Exemplo

Veja um exemplo que mostra como criar 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 cartão 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();
  }