Skip to content

Componentes Reutilizáveis (Svelte Paramétrico)

Um Componente Reutilizável transforma um Widget Svelte em uma peça configurável que passa a aparecer como um novo tipo de widget na biblioteca do Dashboard.

A ideia: quem sabe programar cria o componente uma vez (com o código e um conjunto de opções configuráveis); depois, qualquer pessoa que monta dashboards pode usar e ajustar esse componente sem tocar em código — só preenchendo campos.

TIP

Exemplo: um desenvolvedor cria um "Cartão de Meta" sofisticado e expõe como opções a coluna de valor, a meta e a cor. A partir daí, qualquer usuário adiciona esse cartão em vários dashboards e só escolhe a coluna e a cor — sem ver o código.


👥 Os dois papéis

A funcionalidade separa claramente quem cria de quem usa:

PapelO que faz
AutorEscreve o código, define quais opções ficam configuráveis (o Contrato), define a aparência fixa, e publica o componente.
Quem monta dashboardsEscolhe o componente na biblioteca e preenche os campos configuráveis. Não vê nem edita o código.

🛠️ Criar um componente (Autor)

1. Parta de um Widget Svelte

Crie um Widget Svelte normalmente. Ele usa o mesmo ambiente (Svelte 4, biblioteca app para dados, Tailwind, etc.).

Em vez de fixar a tabela, a coluna ou os textos diretamente no código, leia tudo das opções configuráveis — assim o mesmo código serve para muitos casos.

No editor, clique em "Transformar em paramétrico" para habilitar o Contrato.

2. Defina o Contrato

O Contrato é a lista de opções que ficarão disponíveis para quem usar o componente. Cada opção que o seu código lê precisa estar declarada no Contrato (e vice-versa).

Para cada campo você define o rótulo (nome amigável), a chave (como o código o lê) e o tipo. Os tipos disponíveis estão detalhados na referência do Contrato.

TIP

O Contrato pode ser editado de forma visual (campo a campo) ou em JSON (modo avançado), com validação ao vivo.

Você também pode agrupar campos — campos do mesmo grupo aparecem juntos para quem configura.

3. Defina a Aparência do widget

Na seção "Aparência do widget", o autor decide opções visuais que valem para todas as instâncias:

  • Gráfico com fundo transparente
  • Crescer com o conteúdo

IMPORTANT

A aparência é fixa pelo autor — quem usa o componente não pode alterá-la. Se você criar um cartão com fundo transparente, todas as instâncias serão transparentes.

4. Salve como componente

Clique em "Salvar como componente", informe nome, ícone e categoria. O widget que você estava editando passa a ser uma instância desse componente, e ele entra na biblioteca.


🧑‍💻 Como o código lê o Contrato

O componente recebe duas props injetadas pelo Horus:

  • app — a ponte com os dados e o motor (a mesma API do objeto app do Widget Svelte: fetchMatrix, columnFromString, addFilter, formatValue, etc.).
  • config — um objeto com um valor por campo do Contrato, indexado pela chave de cada campo.

Exemplo completo. Suponha um Contrato com três campos: titulo (Texto), corPrimaria (Cor) e colunaValor (Coluna):

svelte
<script>
  export let app;     // injeção automática do Horus
  export let config;  // valores preenchidos por quem configura o widget

  let total = "…";

  async function carregar() {
    // config.colunaValor chega como string de coluna; converta antes de usar
    const coluna = app.columnFromString(config.colunaValor);
    const dados = await app.fetchMatrix({ columns: [coluna] });
    // formatValue aplica o formato do sistema (moeda, decimais, locale)
    total = app.formatValue(dados.rows?.[0]?.[0], coluna);
  }

  carregar();
</script>

<div class="p-4">
  <h3 style="color: {config.corPrimaria}">{config.titulo}</h3>
  <span class="text-3xl font-bold">{total}</span>
</div>

NOTE

O config chega sempre completo: cada campo declarado no Contrato tem um valor — o que o usuário preencheu, o valor padrão do Contrato, ou um padrão seguro por tipo. O código nunca lê undefined num campo declarado.


📐 Referência do Contrato (manifest)

O Contrato é um JSON com um array fields e, opcionalmente, um objeto display (a aparência fixa):

json
{
  "fields": [
    { "key": "titulo", "type": "text", "label": "Título", "default": "Meu cartão" },
    { "key": "corPrimaria", "type": "color", "label": "Cor principal", "group": "Aparência" },
    { "key": "colunaValor", "type": "column-binding", "label": "Coluna de valor", "filter": "Measure" },
    { "key": "meta", "type": "number", "label": "Meta", "min": 0, "step": 100, "expression": true },
    { "key": "modo", "type": "select", "label": "Modo", "options": [
      { "value": "compacto", "label": "Compacto" },
      { "value": "detalhado", "label": "Detalhado" }
    ]}
  ],
  "display": { "transparentBackground": false, "autoGrow": false }
}

Propriedades comuns a todo campo: key (obrigatória e única), type (obrigatória), label (obrigatória), default, help (texto de ajuda), group (agrupamento visual).

Tipos de campo

typeNa interfacePara quêPropriedades específicas
column-bindingColunaO usuário escolhe uma coluna de dados. O valor chega como string de coluna (use app.columnFromString).multiple (permite várias; o valor vira array), filter ("Measure", "Dimension", "Date" ou "All")
textTextoTítulo, legenda, rótulo curto.
textareaÁrea de TextoTextos longos, descrições.
numberNúmeroValor numérico.min, max, step
rangeIntervaloValor numérico escolhido num controle deslizante.min, max, step
colorCorSeletor de cor (o valor chega como hex, ex.: #4F46E5).
booleanBooleanoLiga/desliga uma opção.
selectSeleçãoEscolha entre opções pré-definidas pelo autor.options (array de { value, label }), forceKind ("segmented" para pílulas ou "list" para dropdown; sem ele a aparência é automática pelo número de opções)
dateDataSeleção de data.
iconÍconeSeletor de ícone (o valor chega como nome iconify, ex.: fa:star).
number-formatFormato numéricoO usuário escolhe um formato de número do sistema; combine com app.formatValue.
svelte_modalEditor Svelte (modal)Abre uma tela de configuração personalizada, feita pelo próprio autor num dos arquivos do componente, para opções complexas.editorComponent (arquivo .svelte do editor), buttonLabel (texto do botão que abre o modal)

Valores padrão

Quando o usuário não preenche um campo, o componente recebe o default declarado no Contrato. Sem default, entra um padrão seguro por tipo: 0 (ou o min) para número e intervalo, false para booleano, #000000 para cor, a primeira opção para seleção, [] ou "" para coluna (conforme multiple), {} para editor modal e "" para os demais.

Fórmulas por campo (expression)

Campos de cor, número, texto, área de texto, intervalo e formato numérico aceitam "expression": true. Com isso, quem configura pode alternar o campo para o modo fórmula e escrever uma expressão JavaScript avaliada em tempo de render (útil para cor condicional, metas dinâmicas, etc.). Se a expressão falhar, vale o valor fixo do campo.


🧪 Testar antes de publicar

Enquanto o editor do componente está aberto, o widget de onde você o abriu reflete as mudanças de código e de Contrato na hora, sem salvar nada. Esse widget mostra o selo "Prévia da edição".

NOTE

A prévia vale apenas para o widget que está sendo editado. As outras instâncias do mesmo componente (no mesmo dashboard ou em outros) continuam mostrando a versão do canal delas. Ao fechar o editor, a prévia termina e o widget volta a mostrar o que está salvo.

Para testar um rascunho salvo num widget específico, use o canal da instância (abaixo).

Canal por instância

Cada instância do componente aponta para um canal, visível no configurador do widget (apenas para quem pode editar o componente):

  • Publicado (no ar) — a versão estável.
  • Rascunho (teste) — o rascunho atual do componente. Útil para validar uma mudança num dashboard real antes de publicar.

No modo de edição do dashboard, instâncias em rascunho exibem o selo "Rascunho".

WARNING

Publicar retorna todas as instâncias ao canal Publicado. O canal Rascunho de uma instância é um estado temporário de teste, e o rascunho de um componente é um só — instâncias em rascunho, em qualquer dashboard, mostram esse mesmo rascunho.


🚀 Publicar, desfazer e descartar

Um componente tem dois canais permanentes, Rascunho e Publicado, e três ações sobre eles:

AçãoO que fazO que acontece com o rascunho
PublicarCopia o rascunho para o canal Publicado, em todas as dashboards onde o componente está instalado.Continua igual ao publicado (sem pendências).
Desfazer publicaçãoO canal Publicado volta à versão publicada anterior (um nível de desfazer), em todas as dashboards.Não muda. Suas edições continuam no rascunho.
Descartar rascunhoO rascunho volta a ser uma cópia do publicado. Use quando quiser abandonar as mudanças em andamento.É sobrescrito pelo conteúdo publicado.

WARNING

Mudanças de código ou de Contrato só chegam a quem usa depois de Publicar. Antes disso, elas existem apenas no rascunho.

O indicador "Afeta N dashboards" mostra onde o componente está instalado (espaço, aplicação, dashboard e canal) — útil para saber o impacto antes de publicar.

NOTE

Um componente recém-criado, ainda sem versão publicada, já renderiza o rascunho onde foi instanciado — justamente para você conseguir testá-lo. Ele só aparece na biblioteca para outras pessoas adicionarem depois de publicado.


🗂️ Gerenciando a biblioteca (HEC)

Além das ações no editor do Dashboard, o console administrativo (HEC) tem a tela Biblioteca de Widgets (menu Recursos e Permissões), pensada para curadoria:

  • Status por componente — se há versão publicada e se existem mudanças não publicadas no rascunho.
  • Publicar / Desfazer publicação / Descartar rascunho — as mesmas ações do editor, com confirmação. Os botões só habilitam quando a ação faz sentido (por exemplo, Publicar só com mudanças pendentes) e só aparecem para quem pode editar o componente.
  • Onde está — a matriz de instalação: espaço, aplicação, dashboard e canal de cada instância.
  • Busca por nome ou categoria.

🧩 Usar um componente (quem monta dashboards)

  1. Na biblioteca de widgets do Dashboard, escolha o componente (aparecem apenas os publicados disponíveis para você).
  2. Adicione ao dashboard como qualquer outro widget.
  3. Na aba "Configurar", preencha os campos do Contrato (vincular colunas, definir textos, cores, etc.).

A aparência é a definida pelo autor. Você não edita o código nem escolhe o canal — apenas configura os valores.


🔐 Quem pode fazer o quê

AçãoQuem pode
Criar, editar (código/Contrato/aparência), publicar, desfazer publicação, descartar rascunho; escolher canal da instânciaAdministradores (do cliente, ou do espaço quando não há cliente) e o suporte Horus
Adicionar o componente e configurar os camposQualquer usuário com permissão de editar dashboards

Para quem só configura, as ações de edição e de canal nem aparecem — apenas os campos configuráveis.


🌐 Onde o componente fica disponível (escopo)

  • Componente do cliente — compartilhado entre todos os espaços daquele cliente. Criar/editar/publicar exige um administrador do cliente (ou suporte Horus).
  • Componente do espaço — privado a um único espaço (usado quando o espaço não pertence a um cliente). Criar/editar/publicar exige um administrador daquele espaço.

Cada usuário vê na biblioteca os componentes do seu cliente mais os do seu espaço.


🤖 Geração por Inteligência Artificial

No painel de IA do editor é possível gerar o componente por linguagem natural: descreva o que quer e a IA produz o código e o Contrato (os campos configuráveis) já acoplados. Você revisa, ajusta e publica.


📌 Regras e detalhes importantes

  • Svelte 4 — mesmo ambiente do Widget Svelte; recursos do Svelte 5 não são suportados.
  • Cada opção lida pelo código precisa estar no Contrato — e cada campo do Contrato deve ser usado pelo código. A ferramenta avisa quando há divergência.
  • A aparência é decisão do autor, fixa para todas as instâncias.
  • Valores padrão — se um campo não for preenchido, o componente usa o padrão definido no Contrato (ou um padrão seguro por tipo), nunca um valor "vazio" inesperado.
  • Publicar é o que propaga mudanças de código/Contrato para os dashboards — e retorna todas as instâncias ao canal Publicado.
  • Fechar o editor sem salvar pede confirmação quando há mudanças pendentes; a prévia da edição termina junto.