Trabaja con chips de menú desplegable

La API de Google Docs te permite crear, insertar, actualizar, leer y administrar chips de menú desplegable de forma programática en documentos de Documentos de Google.

¿Qué son los chips de menú desplegable?

Los chips de menú desplegable en Documentos de Google proporcionan a los usuarios un menú de selección interactivo y personalizable intercalado en el texto del documento. Los usuarios pueden hacer clic en un chip de menú desplegable para seleccionar una opción de una lista predefinida, cada una con su propio texto visible y estilo de color. Los chips de menú desplegable se usan con frecuencia para el seguimiento de proyectos, las actualizaciones de estado, los flujos de trabajo de revisión y las etapas de aprobación.

Con la API de Docs, puedes hacer lo siguiente:

  • Define plantillas de menú desplegable reutilizables con títulos, nombres de opciones y colores personalizados.
  • Inserta chips de menú desplegable en una ubicación de caracteres válida.
  • Actualiza la opción seleccionada de una instancia de chip de menú desplegable individual.
  • Modifica las definiciones de los menús desplegables compartidos en el lugar, y actualiza todos los chips de referencia de forma simultánea.
  • Reemplazar o retirar opciones de forma segura y, al mismo tiempo, mantener la integridad referencial en los chips existentes
  • Borra las plantillas de menú desplegable que no se usen.

Arquitectura: Definiciones e instancias

En la API de Docs, los chips de menú desplegable tienen definiciones e instancias. Una definición establece opciones para una instancia de menú desplegable. Una instancia de menú desplegable es un menú desplegable con el que las personas pueden interactuar y que guarda información sobre las selecciones.

La API de Docs separa la configuración de la plantilla del menú desplegable de las instancias de chips de menú desplegable intercalados:

  1. DropdownDefinition: Es una plantilla a nivel de la pestaña que define el título del menú desplegable y la colección de opciones seleccionables (DropdownOption). No se encuentra en un desplazamiento de caracteres específico en el documento, sino que se almacena en el mapa de definición de la pestaña: document.tabs[].documentTab.dropdownDefinitions.
  2. Menú desplegable: Es una instancia de chip individual intercalada en un elemento de párrafo (ParagraphElement.dropdown). Cada instancia de menú desplegable hace referencia a un dropdownDefinitionId y almacena su propio selectedOptionId activo.

Si modificas un DropdownDefinition (p.ej., agregas una opción o cambias el nombre del título), se actualizarán todos los chips de referencia en la pestaña sin necesidad de actualizar cada elemento individual.

Cada instancia de Dropdown hace un seguimiento de su propio selectedOptionId. La modificación del valor seleccionado de un chip individual solo afecta a esa instancia específica.

Formato de ID y reglas de validación

Los IDs proporcionados por el usuario deben cumplir con especificaciones estrictas de formato, prefijo y longitud:

Identificador Prefijo obligatorio Expresión regular de validación Límite de longitud Ejemplo
ID de definición del menú desplegable (dropdownDefinitionId) kix. ^kix\.[a-zA-Z0-9_-]{2,14}$ De 6 a 18 caracteres kix.review_status
ID de opción de menú desplegable (optionId) dropdownItem. ^dropdownItem\.[a-zA-Z0-9_-]{2,14}$ Entre 15 y 27 caracteres dropdownItem.pending

Genera IDs proporcionados por el usuario

Para generar IDs que cumplan con los requisitos obligatorios de prefijo y expresión regular, usa las siguientes funciones auxiliares. Estos asistentes generan sufijos alfanuméricos en base 36 en minúsculas, que coinciden con el formato que genera la IU de Documentos de Google:

Python

import random
import string

def generate_dropdown_definition_id(suffix_len: int = 8) -> str:
    """Generates a valid user-provided DropdownDefinition ID (6-18 chars, starting with 'kix.')."""
    chars = string.ascii_lowercase + string.digits
    suffix = "".join(random.choices(chars, k=max(2, min(suffix_len, 14))))
    return f"kix.{suffix}"

def generate_dropdown_option_id(suffix_len: int = 8) -> str:
    """Generates a valid user-provided DropdownOption ID (15-27 chars, starting with 'dropdownItem.')."""
    chars = string.ascii_lowercase + string.digits
    suffix = "".join(random.choices(chars, k=max(2, min(suffix_len, 14))))
    return f"dropdownItem.{suffix}"

Java

import java.security.SecureRandom;

public final class DropdownIdGenerator {
  private static final String BASE36_CHARS =
      "abcdefghijklmnopqrstuvwxyz0123456789";
  private static final SecureRandom RANDOM = new SecureRandom();

  private DropdownIdGenerator() {}

  /** Generates a valid user-provided DropdownDefinition ID (6-18 chars, starting with "kix."). */
  public static String generateDefinitionId(int suffixLength) {
    int length = Math.max(2, Math.min(suffixLength, 14));
    StringBuilder sb = new StringBuilder("kix.");
    for (int i = 0; i < length; i++) {
      sb.append(BASE36_CHARS.charAt(RANDOM.nextInt(BASE36_CHARS.length())));
    }
    return sb.toString();
  }

  /** Generates a valid user-provided DropdownOption ID (15-27 chars, starting with "dropdownItem."). */
  public static String generateOptionId(int suffixLength) {
    int length = Math.max(2, Math.min(suffixLength, 14));
    StringBuilder sb = new StringBuilder("dropdownItem.");
    for (int i = 0; i < length; i++) {
      sb.append(BASE36_CHARS.charAt(RANDOM.nextInt(BASE36_CHARS.length())));
    }
    return sb.toString();
  }
}

Cómo crear e insertar chips de menú desplegable

Creación de un solo lote (recomendado)

El patrón más eficiente combina CreateDropdownDefinitionRequest y InsertDropdownRequest en una sola llamada a documents.batchUpdate.

En la siguiente muestra de código, se crea una definición de menú desplegable "Estado de revisión" con tres opciones codificadas por color que usan IDs proporcionados por el usuario y se inserta una instancia al final del documento:

Python

requests = [
    {
        "createDropdownDefinition": {
            "dropdownDefinition": {
                "dropdownDefinitionId": "kix.review_status",
                "dropdownDefinitionProperties": {
                    "title": "Review Status",
                    "options": [
                        {
                            "optionId": "dropdownItem.pending",
                            "displayValue": "Pending Review",
                            "textStyle": {
                                "backgroundColor": {
                                    "color": {"rgbColor": {"red": 0.99, "green": 0.90, "blue": 0.65}}
                                },
                                "foregroundColor": {
                                    "color": {"rgbColor": {"red": 0.45, "green": 0.30, "blue": 0.0}}
                                },
                            },
                        },
                        {
                            "optionId": "dropdownItem.approved",
                            "displayValue": "Approved",
                            "textStyle": {
                                "backgroundColor": {
                                    "color": {"rgbColor": {"red": 0.85, "green": 0.95, "blue": 0.85}}
                                },
                                "foregroundColor": {
                                    "color": {"rgbColor": {"red": 0.08, "green": 0.40, "blue": 0.15}}
                                },
                            },
                        },
                        {
                            "optionId": "dropdownItem.rejected",
                            "displayValue": "Needs Changes",
                            "textStyle": {
                                "backgroundColor": {
                                    "color": {"rgbColor": {"red": 0.98, "green": 0.84, "blue": 0.84}}
                                },
                                "foregroundColor": {
                                    "color": {"rgbColor": {"red": 0.65, "green": 0.10, "blue": 0.10}}
                                },
                            },
                        },
                    ],
                },
            }
        }
    },
    {
        "insertDropdown": {
            "endOfSegmentLocation": {},
            "dropdownDefinitionId": "kix.review_status",
            "selectedOptionId": "dropdownItem.pending",
        }
    },
]

result = service.documents().batchUpdate(
    documentId=DOCUMENT_ID,
    body={"requests": requests}
).execute()

Java

List<Request> requests = new ArrayList<>();

List<DropdownOption> options = Arrays.asList(
    new DropdownOption()
        .setOptionId("dropdownItem.pending")
        .setDisplayValue("Pending Review")
        .setTextStyle(new TextStyle()
            .setBackgroundColor(new OptionalColor().setColor(
                new Color().setRgbColor(new RgbColor().setRed(0.99f).setGreen(0.90f).setBlue(0.65f))))
            .setForegroundColor(new OptionalColor().setColor(
                new Color().setRgbColor(new RgbColor().setRed(0.45f).setGreen(0.30f).setBlue(0.0f))))),
    new DropdownOption()
        .setOptionId("dropdownItem.approved")
        .setDisplayValue("Approved")
        .setTextStyle(new TextStyle()
            .setBackgroundColor(new OptionalColor().setColor(
                new Color().setRgbColor(new RgbColor().setRed(0.85f).setGreen(0.95f).setBlue(0.85f))))
            .setForegroundColor(new OptionalColor().setColor(
                new Color().setRgbColor(new RgbColor().setRed(0.08f).setGreen(0.40f).setBlue(0.15f))))),
    new DropdownOption()
        .setOptionId("dropdownItem.rejected")
        .setDisplayValue("Needs Changes")
        .setTextStyle(new TextStyle()
            .setBackgroundColor(new OptionalColor().setColor(
                new Color().setRgbColor(new RgbColor().setRed(0.98f).setGreen(0.84f).setBlue(0.84f))))
            .setForegroundColor(new OptionalColor().setColor(
                new Color().setRgbColor(new RgbColor().setRed(0.65f).setGreen(0.10f).setBlue(0.10f))))));

DropdownDefinition definition = new DropdownDefinition()
    .setDropdownDefinitionId("kix.review_status")
    .setDropdownDefinitionProperties(new DropdownDefinitionProperties()
        .setTitle("Review Status")
        .setOptions(options));

requests.add(new Request().setCreateDropdownDefinition(
    new CreateDropdownDefinitionRequest().setDropdownDefinition(definition)));

requests.add(new Request().setInsertDropdown(
    new InsertDropdownRequest()
        .setEndOfSegmentLocation(new EndOfSegmentLocation())
        .setDropdownDefinitionId("kix.review_status")
        .setSelectedOptionId("dropdownItem.pending")));

BatchUpdateDocumentRequest body = new BatchUpdateDocumentRequest().setRequests(requests);
BatchUpdateDocumentResponse response = docsService.documents()
    .batchUpdate(DOCUMENT_ID, body)
    .execute();

Reglas y límites de validación de menús desplegables

Cuando crees o modifiques definiciones de menús desplegables, se aplicarán las siguientes restricciones:

  • Recuento de opciones: Una definición de menú desplegable debe tener entre 2 y 50 opciones.
  • Longitud del título: La definición title no puede estar vacía y no debe superar los 200 caracteres.
  • Longitud del valor para mostrar: El displayValue de cada opción no puede estar vacío y no debe superar los 200 caracteres.
  • Diseño de opciones: Solo se admiten foregroundColor y backgroundColor en DropdownOption.textStyle. Si se configuran otras propiedades de diseño, se devuelve un error 400 Bad Request.
  • Selección predeterminada: En InsertDropdownRequest, si se omite selectedOptionId, el chip se establece de forma predeterminada en la primera opción definida en DropdownDefinition.

Actualiza la selección de un chip de menú desplegable individual

Para cambiar la opción seleccionada de una instancia de chip de menú desplegable existente sin alterar su plantilla ni otros chips, usa UpdateDropdownPropertiesRequest:

  • dropdownId: (Obligatorio) ID de la instancia de chip de menú desplegable específica que se actualizará.
  • tabId: ID de la pestaña que contiene el menú desplegable (si se omite, se usa la primera pestaña de forma predeterminada).
  • fields: Configurado como "selectedOptionId".

En la siguiente muestra de código, se actualiza la selección activa de un chip de menú desplegable a "dropdownItem.approved":

Python

requests = [
    {
        "updateDropdownProperties": {
            "dropdownId": "kix.chip_abc1",
            "tabId": "t.0",
            "dropdownProperties": {
                "selectedOptionId": "dropdownItem.approved",
            },
            "fields": "selectedOptionId",
        }
    }
]

result = service.documents().batchUpdate(
    documentId=DOCUMENT_ID,
    body={"requests": requests}
).execute()

Java

List<Request> requests = new ArrayList<>();
requests.add(new Request().setUpdateDropdownProperties(
    new UpdateDropdownPropertiesRequest()
        .setDropdownId("kix.chip_abc1")
        .setTabId("t.0")
        .setDropdownProperties(new DropdownProperties()
            .setSelectedOptionId("dropdownItem.approved"))
        .setFields("selectedOptionId")));

BatchUpdateDocumentRequest body = new BatchUpdateDocumentRequest().setRequests(requests);
BatchUpdateDocumentResponse response = docsService.documents()
    .batchUpdate(DOCUMENT_ID, body)
    .execute();

Actualiza las definiciones y opciones de los menús desplegables

Para modificar la plantilla en sí (título, lista de opciones, etiquetas de visualización o colores), usa UpdateDropdownDefinitionPropertiesRequest. Si actualizas una definición, se actualizarán automáticamente todos los chips de menú desplegable que la referencien en la pestaña.

Reemplazo de la lista completa de opciones

Cuando dropdownDefinitionProperties.options se incluye en la máscara de fields, la solicitud realiza un reemplazo de lista completa. Debes proporcionar la lista completa de opciones en el orden elegido. El servidor compara la lista entrante con la definición actual:

  • Nueva opción: Si se incluye una opción sin un optionId (o con un optionId proporcionado por un usuario nuevo), se agrega a la definición.
  • Opción actualizada: Si proporcionas un optionId existente con actualizaciones de displayValue o textStyle modificados, se actualizará esa opción.
  • Opciones reordenadas: La lista de opciones se guarda en la secuencia exacta que se proporcionó en la solicitud.
  • Opción borrada: Si se omite un optionId existente, se borra esa opción de la definición.

Integridad referencial y reemplazo de opciones

Si se selecciona una opción que se borrará en algún chip de menú desplegable del documento, debes proporcionar una asignación en selectedOptionIdReplacements (map<string, string>). Las claves son los IDs de las opciones que se borrarán y los valores son los IDs de las opciones de reemplazo.

En la siguiente muestra de código, se muestra cómo actualizar una definición de menú desplegable con reemplazos de opciones:

Python

requests = [
    {
        "updateDropdownDefinitionProperties": {
            "dropdownDefinitionId": "kix.review_status",
            "tabId": "t.0",
            "dropdownDefinitionProperties": {
                "title": "Editorial Review Status",
                "options": [
                    {
                        "optionId": "dropdownItem.approved",
                        "displayValue": "Approved",
                        "textStyle": {
                            "backgroundColor": {
                                "color": {"rgbColor": {"red": 0.85, "green": 0.95, "blue": 0.85}}
                            },
                            "foregroundColor": {
                                "color": {"rgbColor": {"red": 0.08, "green": 0.40, "blue": 0.15}}
                            },
                        },
                    },
                    {
                        "optionId": "dropdownItem.rejected",
                        "displayValue": "Changes Requested",
                        "textStyle": {
                            "backgroundColor": {
                                "color": {"rgbColor": {"red": 0.98, "green": 0.84, "blue": 0.84}}
                            },
                            "foregroundColor": {
                                "color": {"rgbColor": {"red": 0.65, "green": 0.10, "blue": 0.10}}
                            },
                        },
                    },
                ],
            },
            "selectedOptionIdReplacements": {
                "dropdownItem.pending": "dropdownItem.rejected"
            },
            "fields": "title,options",
        }
    }
]

result = service.documents().batchUpdate(
    documentId=DOCUMENT_ID,
    body={"requests": requests}
).execute()

Java

List<DropdownOption> updatedOptions = Arrays.asList(
    new DropdownOption()
        .setOptionId("dropdownItem.approved")
        .setDisplayValue("Approved")
        .setTextStyle(new TextStyle()
            .setBackgroundColor(new OptionalColor().setColor(
                new Color().setRgbColor(new RgbColor().setRed(0.85f).setGreen(0.95f).setBlue(0.85f))))
            .setForegroundColor(new OptionalColor().setColor(
                new Color().setRgbColor(new RgbColor().setRed(0.08f).setGreen(0.40f).setBlue(0.15f))))),
    new DropdownOption()
        .setOptionId("dropdownItem.rejected")
        .setDisplayValue("Changes Requested")
        .setTextStyle(new TextStyle()
            .setBackgroundColor(new OptionalColor().setColor(
                new Color().setRgbColor(new RgbColor().setRed(0.98f).setGreen(0.84f).setBlue(0.84f))))
            .setForegroundColor(new OptionalColor().setColor(
                new Color().setRgbColor(new RgbColor().setRed(0.65f).setGreen(0.10f).setBlue(0.10f))))));

Map<String, String> replacements = new HashMap<>();
replacements.put("dropdownItem.pending", "dropdownItem.rejected");

List<Request> requests = new ArrayList<>();
requests.add(new Request().setUpdateDropdownDefinitionProperties(
    new UpdateDropdownDefinitionPropertiesRequest()
        .setDropdownDefinitionId("kix.review_status")
        .setTabId("t.0")
        .setDropdownDefinitionProperties(new DropdownDefinitionProperties()
            .setTitle("Editorial Review Status")
            .setOptions(updatedOptions))
        .setSelectedOptionIdReplacements(replacements)
        .setFields("title,options")));

BatchUpdateDocumentRequest body = new BatchUpdateDocumentRequest().setRequests(requests);
BatchUpdateDocumentResponse response = docsService.documents()
    .batchUpdate(DOCUMENT_ID, body)
    .execute();

Borra una definición de menú desplegable

Para quitar una plantilla de menú desplegable sin usar, usa DeleteDropdownDefinitionRequest.

En la siguiente muestra de código, se indica cómo borrar una definición de menú desplegable:

Python

requests = [
    {
        "deleteDropdownDefinition": {
            "dropdownDefinitionId": "kix.review_status",
            "tabId": "t.0",
        }
    }
]

result = service.documents().batchUpdate(
    documentId=DOCUMENT_ID,
    body={"requests": requests}
).execute()

Java

List<Request> requests = new ArrayList<>();
requests.add(new Request().setDeleteDropdownDefinition(
    new DeleteDropdownDefinitionRequest()
        .setDropdownDefinitionId("kix.review_status")
        .setTabId("t.0")));

BatchUpdateDocumentRequest body = new BatchUpdateDocumentRequest().setRequests(requests);
BatchUpdateDocumentResponse response = docsService.documents()
    .batchUpdate(DOCUMENT_ID, body)
    .execute();

Manejo de errores y solución de problemas

Condiciones de error y resoluciones comunes:

Código de estado Causa Solución
400 INVALID_ARGUMENT El prefijo del ID o el formato de la expresión regular no son válidos. Asegúrate de que dropdownDefinitionId comience con kix. (de 6 a 18 caracteres) y que optionId comience con dropdownItem. (de 15 a 27 caracteres).
400 INVALID_ARGUMENT El recuento de opciones está fuera de los límites. Una definición de menú desplegable debe contener entre 2 y 50 opciones.
400 INVALID_ARGUMENT El título o el valor para mostrar están vacíos o superan los 200 caracteres. Proporciona una cadena no vacía de entre 1 y 200 caracteres.
400 INVALID_ARGUMENT Propiedad de estilo de texto no admitida en la opción. Solo se admiten foregroundColor y backgroundColor en las opciones del menú desplegable. Quita la fuente, el tamaño o cualquier otro atributo.
400 INVALID_ARGUMENT Falta la asignación de reemplazo de la opción al borrar. Cuando borres una opción seleccionada por algún chip, proporciona un reemplazo válido en selectedOptionIdReplacements.
400 INVALID_ARGUMENT Se intentó borrar una definición en uso. Borra o reorienta todas las instancias de chips de menú desplegable que hagan referencia a la definición antes de borrarla.
400 INVALID_ARGUMENT Se duplicó el ID proporcionado por el usuario. Asegúrate de que los IDs proporcionados por el usuario sean únicos en la pestaña del documento.