O tipo de campo json do EmDash armazena dados estruturados arbitrários, editados por padrão através de um input de texto de linha única que aceita JSON bruto. Field Kit é um plugin oficial que fornece quatro widgets componíveis para campos json, configurados inteiramente através de options de seed para que os construtores de sites possam usá-los apenas com o esquema de seed.
Instalação
Instale o pacote do npm:
npm i @emdash-cms/plugin-field-kit
A seguinte configuração registra o plugin:
import { defineConfig } from "astro/config";
import emdash from "emdash/astro";
import { fieldKitPlugin } from "@emdash-cms/plugin-field-kit";
export default defineConfig({
integrations: [
emdash({
plugins: [fieldKitPlugin()],
}),
],
});
Anexe um widget a qualquer campo json definindo widget como field-kit:<name>. A seguinte definição de campo usa o widget list:
{
"slug": "ingredients",
"type": "json",
"widget": "field-kit:list",
"options": { "fields": [...] }
}
Widgets
| Widget | Uso | Valor armazenado |
|---|---|---|
object-form | Formulário inline para objetos JSON simples | { key: value, ... } |
list | Editor de array ordenado com adicionar / remover / reordenar | [{ ... }, ...] |
grid | Matriz de linhas × colunas | { rowKey: { colKey: value } } |
tags | Input de chip/tag de forma livre | ["tag1", "tag2"] |
Se um widget estiver faltando suas options necessárias (por exemplo, fields para object-form/list, ou rows/columns para grid), o editor renderiza um aviso inline “Widget mal configurado” em vez de um input quebrado — útil ao iterar em esquemas de seed.
object-form
Renderiza um grupo de subcampos tipados que são armazenados como um único objeto JSON. Bom para dados estruturados de forma fixa como informações nutricionais ou de contato. A seguinte definição de campo configura um objeto nutricional:
{
"slug": "nutrition",
"type": "json",
"widget": "field-kit:object-form",
"options": {
"collapsed": false,
"fields": [
{ "key": "calories", "label": "Calories", "type": "number", "suffix": "kcal" },
{ "key": "protein", "label": "Protein", "type": "number", "suffix": "g" },
{ "key": "fat", "label": "Fat", "type": "number", "suffix": "g" },
{ "key": "carbs", "label": "Carbs", "type": "number", "suffix": "g" }
]
}
}
Valor armazenado: { "calories": 250, "protein": 12.5, "fat": 8, "carbs": 30 }.
| Opção | Tipo | Padrão | Descrição |
|---|---|---|---|
fields | SubFieldDef[] | (obrigatório) | Definições de subcampos — veja Subcampos. |
collapsed | boolean | false | Renderizar o grupo recolhido por padrão. |
helpText | string | — | Texto de ajuda exibido abaixo do widget. |
list
Um editor de array ordenado com controles de adicionar, remover e reordenar. Cada linha é um objeto JSON cuja forma é definida por fields. O cabeçalho da linha mostra um resumo renderizado a partir de um template estilo Mustache. A seguinte definição de campo configura uma lista de ingredientes:
{
"slug": "ingredients",
"type": "json",
"widget": "field-kit:list",
"options": {
"itemLabel": "Ingredient",
"min": 1,
"max": 50,
"sortable": true,
"summary": "{{name}} — {{amount}}",
"fields": [
{ "key": "name", "label": "Name", "type": "text", "required": true },
{ "key": "amount", "label": "Amount", "type": "text" },
{ "key": "optional", "label": "Optional", "type": "boolean" }
]
}
}
O valor armazenado é um array de objetos de linha:
[
{ "name": "Flour", "amount": "500g", "optional": false },
{ "name": "Butter", "amount": "200g", "optional": false }
]
| Opção | Tipo | Padrão | Descrição |
|---|---|---|---|
fields | SubFieldDef[] | (obrigatório) | Definições de subcampos para cada linha. |
itemLabel | string | "Item" | Label singular para uma linha (usado no botão “Adicionar” e títulos de linha de fallback). |
min | number | — | Número mínimo de itens. Abaixo disso, o botão remover se esconde. |
max | number | — | Número máximo de itens. Nesta contagem, o botão adicionar se esconde. |
sortable | boolean | true | Mostrar botões de reordenar para cima/baixo. |
summary | string | — | Template Mustache renderizado como o título de linha recolhida. Veja Templates de resumo. |
helpText | string | — | Texto de ajuda exibido abaixo do widget. |
grid
Uma matriz bidimensional de linhas × colunas. Cada célula pode ser um alternador, input de texto, input numérico ou seleção. Útil para matrizes como disponibilidade sazonal, tabelas de preços ou comparações de recursos. A seguinte definição de campo configura uma grade de disponibilidade sazonal:
{
"slug": "availability",
"type": "json",
"widget": "field-kit:grid",
"options": {
"cell": "toggle",
"rows": [
{ "key": "berries", "label": "Berries" },
{ "key": "stoneFruit", "label": "Stone fruit" },
{ "key": "citrus", "label": "Citrus" }
],
"columns": [
{ "key": "spring", "label": "Spring" },
{ "key": "summer", "label": "Summer" },
{ "key": "autumn", "label": "Autumn" },
{ "key": "winter", "label": "Winter" }
]
}
}
O valor armazenado é um objeto indexado por linha, depois por coluna:
{
"berries": { "spring": false, "summer": true, "autumn": false, "winter": false },
"stoneFruit": { "spring": false, "summer": true, "autumn": true, "winter": false },
"citrus": { "spring": false, "summer": false, "autumn": true, "winter": true }
}
| Opção | Tipo | Padrão | Descrição |
|---|---|---|---|
rows | GridAxisDef[] | (obrigatório) | Definições de linhas: { key, label, image? }. |
columns | GridAxisDef[] | (obrigatório) | Definições de colunas: { key, label, image? }. |
cell | "toggle" | "text" | "number" | "select" | "toggle" | Tipo de input de célula, aplicado uniformemente a cada célula. |
cellOptions | string[] | Array<{ label, value }> | [] | Obrigatório quando cell é "select". |
helpText | string | — | Texto de ajuda exibido abaixo do widget. |
tags
Um input estilo chip para arrays de strings. Suporta uma lista fixa de suggestions, valores personalizados de forma livre (alternáveis), transformações de maiúsculas/minúsculas e um max opcional. A seguinte definição de campo configura um input de tags de palavras-chave:
{
"slug": "keywords",
"type": "json",
"widget": "field-kit:tags",
"options": {
"placeholder": "Add a keyword…",
"max": 10,
"transform": "lowercase",
"allowCustom": true,
"suggestions": ["vegan", "vegetarian", "gluten-free", "dairy-free", "nut-free"]
}
}
Valor armazenado: ["vegan", "gluten-free"].
Pressione Enter ou , para confirmar uma tag. Backspace em um input vazio remove a última tag. Tags duplicadas são ignoradas silenciosamente.
| Opção | Tipo | Padrão | Descrição |
|---|---|---|---|
placeholder | string | "Add..." | Placeholder de input exibido quando não há tags presentes. |
max | number | — | Número máximo de tags. O input se esconde no limite. |
suggestions | string[] | [] | Sugestões de autocomplete exibidas via <datalist>. |
allowCustom | boolean | true | Quando false, apenas valores de suggestions podem ser adicionados. |
transform | "none" | "lowercase" | "uppercase" | "trim" | "none" | Normalizar tags à medida que são adicionadas. |
helpText | string | — | Texto de ajuda exibido abaixo do widget. |
Subcampos
object-form e list aceitam um array options.fields de definições de subcampos tipados. Cada entrada tem uma key (a chave do objeto JSON em que escreve), um label, um type e extras específicos do tipo.
| Tipo de subcampo | Renderizado como | Extras notáveis |
|---|---|---|
text | Input de linha única | placeholder |
textarea | Input multilinha | rows (padrão 3), placeholder |
number | Input numérico | min, max, step, prefix, suffix, placeholder |
boolean | Interruptor de alternância | — |
select | Menu suspenso | options: string[] | Array<{ label, value }>, placeholder |
date | Input de data | — |
color | Seletor de cor nativo emparelhado com um input de texto hexadecimal | — |
url | Input de URL (HTML5 type="url") | placeholder |
Propriedades comuns em cada subcampo: required, helpText, defaultValue.
Templates de resumo
O widget list renderiza cada linha recolhida usando um template estilo Mustache em options.summary. {{key}} é substituído pelo valor da linha para essa chave (convertido para string). Valores falsy retornam para "{itemLabel} {n}". O seguinte template combina duas chaves:
"summary": "{{name}} — {{amount}}"
Renderiza linhas como Flour — 500g. O template é substituição de string simples — sem HTML, sem expressões aninhadas.
Durabilidade de dados
Os widgets do Field Kit armazenam JSON simples na coluna existente do campo, usando apenas essa coluna. Se você remover @emdash-cms/plugin-field-kit da sua configuração, os dados permanecem válidos — o campo reverte para o input de texto json padrão.
Isso se aplica mesmo quando você muda a forma do widget: chaves desconhecidas em objetos armazenados são preservadas na próxima gravação, para que você possa evoluir um esquema sem perder dados capturados sob um conjunto de campos mais antigo.
Veja também
- Visão geral do plugin — como os plugins do EmDash funcionam.
- Escolhendo um formato de plugin — escreva seus próprios widgets de campo se o Field Kit não se adequar.
- Discussão #571 — a proposta que levou a este plugin.