El tipo de campo json de EmDash almacena datos estructurados arbitrarios, editados de forma predeterminada a través de una entrada de texto de una sola línea que acepta JSON sin procesar. Field Kit es un plugin oficial que incluye cuatro widgets componibles para campos json, configurados completamente mediante options de seed para que los constructores de sitios puedan usarlos solo con el esquema de seed.
Instalación
Instala el paquete desde npm:
npm i @emdash-cms/plugin-field-kit
La siguiente configuración registra el 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()],
}),
],
});
Adjunta un widget a cualquier campo json estableciendo widget en field-kit:<name>. La siguiente definición de campo usa el widget list:
{
"slug": "ingredients",
"type": "json",
"widget": "field-kit:list",
"options": { "fields": [...] }
}
Widgets
| Widget | Uso | Valor almacenado |
|---|---|---|
object-form | Formulario en línea para objetos JSON planos | { key: value, ... } |
list | Editor de array ordenado con agregar / eliminar / reordenar | [{ ... }, ...] |
grid | Matriz de filas × columnas | { rowKey: { colKey: value } } |
tags | Entrada de chip/etiqueta de forma libre | ["tag1", "tag2"] |
Si a un widget le faltan sus options requeridas (por ejemplo, fields para object-form/list, o rows/columns para grid), el editor muestra una advertencia en línea “Widget mal configurado” en lugar de una entrada rota — útil mientras se itera en esquemas de seed.
object-form
Renderiza un grupo de subcampos tipados que se almacenan como un único objeto JSON. Bueno para datos estructurados de forma fija como información nutricional o de contacto. La siguiente definición de campo configura un objeto de nutrición:
{
"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 almacenado: { "calories": 250, "protein": 12.5, "fat": 8, "carbs": 30 }.
| Opción | Tipo | Predeterminado | Descripción |
|---|---|---|---|
fields | SubFieldDef[] | (requerido) | Definiciones de subcampos — ver Subcampos. |
collapsed | boolean | false | Renderizar el grupo contraído por defecto. |
helpText | string | — | Texto de ayuda mostrado debajo del widget. |
list
Un editor de array ordenado con controles de agregar, eliminar y reordenar. Cada fila es un objeto JSON cuya forma está definida por fields. El encabezado de fila muestra un resumen renderizado desde una plantilla estilo Mustache. La siguiente definición de campo configura una 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" }
]
}
}
El valor almacenado es un array de objetos de fila:
[
{ "name": "Flour", "amount": "500g", "optional": false },
{ "name": "Butter", "amount": "200g", "optional": false }
]
| Opción | Tipo | Predeterminado | Descripción |
|---|---|---|---|
fields | SubFieldDef[] | (requerido) | Definiciones de subcampos para cada fila. |
itemLabel | string | "Item" | Etiqueta singular para una fila (usada en el botón “Agregar” y títulos de fila de respaldo). |
min | number | — | Número mínimo de elementos. Por debajo de esto, el botón eliminar se oculta. |
max | number | — | Número máximo de elementos. En este recuento, el botón agregar se oculta. |
sortable | boolean | true | Mostrar botones de reordenar arriba/abajo. |
summary | string | — | Plantilla Mustache renderizada como el título de fila contraída. Ver Plantillas de resumen. |
helpText | string | — | Texto de ayuda mostrado debajo del widget. |
grid
Una matriz bidimensional de filas × columnas. Cada celda puede ser un interruptor, entrada de texto, entrada numérica o selección. Útil para matrices como disponibilidad estacional, tablas de precios o comparaciones de características. La siguiente definición de campo configura una cuadrícula de disponibilidad estacional:
{
"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" }
]
}
}
El valor almacenado es un objeto indexado por fila, luego por columna:
{
"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 }
}
| Opción | Tipo | Predeterminado | Descripción |
|---|---|---|---|
rows | GridAxisDef[] | (requerido) | Definiciones de filas: { key, label, image? }. |
columns | GridAxisDef[] | (requerido) | Definiciones de columnas: { key, label, image? }. |
cell | "toggle" | "text" | "number" | "select" | "toggle" | Tipo de entrada de celda, aplicado uniformemente a cada celda. |
cellOptions | string[] | Array<{ label, value }> | [] | Requerido cuando cell es "select". |
helpText | string | — | Texto de ayuda mostrado debajo del widget. |
tags
Una entrada estilo chip para arrays de cadenas. Admite una lista fija de suggestions, valores personalizados de forma libre (conmutable), transformaciones de mayúsculas/minúsculas y un max opcional. La siguiente definición de campo configura una entrada de etiquetas de palabras clave:
{
"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 almacenado: ["vegan", "gluten-free"].
Presiona Enter o , para confirmar una etiqueta. Backspace en una entrada vacía elimina la última etiqueta. Las etiquetas duplicadas se ignoran silenciosamente.
| Opción | Tipo | Predeterminado | Descripción |
|---|---|---|---|
placeholder | string | "Add..." | Marcador de posición de entrada mostrado cuando no hay etiquetas presentes. |
max | number | — | Número máximo de etiquetas. La entrada se oculta en el límite. |
suggestions | string[] | [] | Sugerencias de autocompletado mostradas a través de un <datalist>. |
allowCustom | boolean | true | Cuando es false, solo se pueden agregar valores de suggestions. |
transform | "none" | "lowercase" | "uppercase" | "trim" | "none" | Normalizar etiquetas a medida que se agregan. |
helpText | string | — | Texto de ayuda mostrado debajo del widget. |
Subcampos
object-form y list aceptan un array options.fields de definiciones de subcampos tipados. Cada entrada tiene un key (la clave del objeto JSON en la que escribe), un label, un type y extras específicos del tipo.
| Tipo de subcampo | Se renderiza como | Extras notables |
|---|---|---|
text | Entrada de una línea | placeholder |
textarea | Entrada multilínea | rows (predeterminado 3), placeholder |
number | Entrada numérica | min, max, step, prefix, suffix, placeholder |
boolean | Interruptor de alternancia | — |
select | Desplegable | options: string[] | Array<{ label, value }>, placeholder |
date | Entrada de fecha | — |
color | Selector de color nativo emparejado con una entrada de texto hexadecimal | — |
url | Entrada URL (HTML5 type="url") | placeholder |
Propiedades comunes en cada subcampo: required, helpText, defaultValue.
Plantillas de resumen
El widget list renderiza cada fila contraída usando una plantilla estilo Mustache en options.summary. {{key}} se reemplaza con el valor de la fila para esa clave (convertido a una cadena). Los valores falsos vuelven a "{itemLabel} {n}". La siguiente plantilla combina dos claves:
"summary": "{{name}} — {{amount}}"
Renderiza filas como Flour — 500g. La plantilla es sustitución de cadena simple — sin HTML, sin expresiones anidadas.
Durabilidad de datos
Los widgets de Field Kit almacenan JSON simple en la columna existente del campo, usando solo esa columna. Si eliminas @emdash-cms/plugin-field-kit de tu configuración, los datos permanecen válidos — el campo vuelve a la entrada de texto json predeterminada.
Esto se aplica incluso cuando cambias la forma del widget: las claves desconocidas en objetos almacenados se preservan en la siguiente escritura, por lo que puedes evolucionar un esquema sin perder datos capturados bajo un conjunto de campos más antiguo.
Ver también
- Resumen de plugins — cómo funcionan los plugins de EmDash.
- Elegir un formato de plugin — escribe tus propios widgets de campo si Field Kit no se ajusta.
- Discusión #571 — la propuesta que condujo a este plugin.