Ir al contenido
Docxcelerate

Fundamentos

Documentos y nodos

Un documento es un árbol de componentes, cada uno toma sus datos como estado y devuelve los nodos que quiere.

Un documento es una plantilla más un tipo de datos. La plantilla es un árbol de componentes; cada uno devuelve nodos, y resolver el árbol con datos produce un DocumentModel — JSON puro, sin que eso implique nada sobre el renderizado.

Un componente toma sus datos como estado

import { Paragraph, useState } from "docxcelerate/template";
import type { DocumentData } from "../types.ts";

export const Greeting: Paragraph = () => {
  const [state] = useState((data: DocumentData) => ({
    name: data.residentName,
  }));

  return <Paragraph id="greeting">Dear {state.name},</Paragraph>;
};

useState es el lugar por donde los datos entran en un componente, y el único. Todo lo que viene después lee el estado. Es una regla con un motivo: como cada dependencia pasa por una sola declaración, lo que un componente necesita queda escrito en vez de repartido por el código que lo usa.

El inicializador es TypeScript corriente. No hay ningún lenguaje de plantillas, así que las condiciones, el formato y los imports están simplemente disponibles.

Paragraph nombra a la vez el elemento y el tipo de componente — un valor y un tipo pueden compartir nombre —, de modo que const Greeting: Paragraph dice qué produce esto, y devolver un <Section> desde ahí es un error de compilación.

Los datos también pueden llegar como props

Un componente al que se le da lo que necesita no tiene que ir a buscarlo:

export const Arrears: Paragraph<{ amount: number }> = ({ amount }) => {
  const { currency } = useFormat();

  return <Paragraph id="arrears">You owe {currency(amount)}.</Paragraph>;
};

Alguien tiene que leer los datos primero, así que un componente solo de props vive bajo un padre que los tomó como estado. Usa props para componentes que quieras reutilizar con valores distintos, y estado para componentes que saben a qué documento pertenecen.

Las decisiones son un if corriente

export const Balance: Paragraph = () => {
  const [state] = useState((data: DocumentData) => ({
    settled: data.balanceDue === 0,
  }));

  if (state.settled) {
    return <Paragraph id="settled">Nothing outstanding.</Paragraph>;
  }

  return <Paragraph id="arrears">A balance remains.</Paragraph>;
};

Dale a cada rama su propio id. El documento resuelto registra entonces cuál le tocó a este destinatario, que es la diferencia entre un documento que puedes auditar y uno que solo puedes mirar.

Hay algo que conviene saber sobre las bifurcaciones antes de publicar en un motor: mira qué cambia al publicar.

Los ids

Un id es cómo un motor direcciona un nodo y cómo los artefactos de compilación siguen siendo diffables entre ejecuciones, así que trata un renombrado como un cambio incompatible.

Puedes omitirlo. Un nodo sin id toma uno de donde está, y eso es lo que evita que las ramas y los bucles te obliguen a inventar nombres. Lo que no puedes es usar uno dos veces: dos nodos reclamando un id es un error, notificado con ambas posiciones, y no una carrera que gana el último.

Las secciones anidan, los nodos no

Section es la única construcción que anida. Su título pasa al esquema del documento:

<Section id="opening" title="Opening">
  <Greeting />
  <Offer />
</Section>

Mira Section para saber qué puede contener una sección y hasta qué profundidad.

Hooks

Los hooks son cómo un componente alcanza la compilación que lo rodea:

HookQué te da
useStateLos datos, tomados una vez y conservados
useSharedUn valor dejado para los componentes que se renderizan después
useSetPromptsPrompts para el nodo que produce — lo que lo hace dinámico
useSetPlaceholdersLo que las vistas previas muestran en lugar del contenido generado
usePlaceholderDataNombres, fechas y cifras de relleno para las vistas previas
useFormatMonedas, fechas, listas y plurales según la configuración regional
useAvailableTokensEl presupuesto de tokens que asignó esta compilación
useDeriverEjecuta ahora un derivador registrado

Se ejecutan en orden de llamada, así que todo hook debe alcanzarse antes del primer await y antes de cualquier bifurcación — la misma regla que usa React, por el mismo motivo. Llamar a uno después de un await es un error que lo dice, en lugar de engancharse en silencio al siguiente componente que se renderice.

Compilar el documento

import { buildDocument } from "docxcelerate";

const doc = await buildDocument(documentTemplate, data);

El resultado es un DocumentModel: una schemaVersion, un id, un title y un array nodes. No contiene estilos ni maquetación — eso lo aplica más tarde el renderizador que lo consuma.

Para un proyecto de documento definido con defineDocumentProject, es preferible buildProjectPreviewDocument, que aplica el estilo del proyecto y resuelve los nodos dinámicos a sus marcadores de posición:

import { buildProjectPreviewDocument } from "docxcelerate";

const doc = await buildProjectPreviewDocument(project);

Es exactamente lo que llama la app de vista previa, así que lo que compilas en código coincide con lo que viste en el navegador.


Editar esta página en GitHub ↗ Leer esta página en Markdown