create-email-template 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +356 -0
- package/dist/components/builder/block-palette.d.ts +4 -0
- package/dist/components/builder/block-palette.d.ts.map +1 -0
- package/dist/components/builder/canvas.d.ts +4 -0
- package/dist/components/builder/canvas.d.ts.map +1 -0
- package/dist/components/builder/editable-block-renderer.d.ts +7 -0
- package/dist/components/builder/editable-block-renderer.d.ts.map +1 -0
- package/dist/components/builder/email-builder.d.ts +5 -0
- package/dist/components/builder/email-builder.d.ts.map +1 -0
- package/dist/components/builder/inline-text-editor.d.ts +15 -0
- package/dist/components/builder/inline-text-editor.d.ts.map +1 -0
- package/dist/components/builder/properties-panel.d.ts +8 -0
- package/dist/components/builder/properties-panel.d.ts.map +1 -0
- package/dist/components/builder/selection-toolbar.d.ts +12 -0
- package/dist/components/builder/selection-toolbar.d.ts.map +1 -0
- package/dist/components/builder/sortable-item.d.ts +10 -0
- package/dist/components/builder/sortable-item.d.ts.map +1 -0
- package/dist/components/builder/variables-info-dialog.d.ts +7 -0
- package/dist/components/builder/variables-info-dialog.d.ts.map +1 -0
- package/dist/components/ui/button.d.ts +10 -0
- package/dist/components/ui/button.d.ts.map +1 -0
- package/dist/components/ui/dialog.d.ts +18 -0
- package/dist/components/ui/dialog.d.ts.map +1 -0
- package/dist/components/ui/input.d.ts +4 -0
- package/dist/components/ui/input.d.ts.map +1 -0
- package/dist/components/ui/label.d.ts +4 -0
- package/dist/components/ui/label.d.ts.map +1 -0
- package/dist/components/ui/sheet.d.ts +15 -0
- package/dist/components/ui/sheet.d.ts.map +1 -0
- package/dist/config/defaults.d.ts +3 -0
- package/dist/config/defaults.d.ts.map +1 -0
- package/dist/config/types.d.ts +125 -0
- package/dist/config/types.d.ts.map +1 -0
- package/dist/core/blocks.d.ts +33 -0
- package/dist/core/blocks.d.ts.map +1 -0
- package/dist/core/default-blocks.d.ts +2 -0
- package/dist/core/default-blocks.d.ts.map +1 -0
- package/dist/core/id.d.ts +2 -0
- package/dist/core/id.d.ts.map +1 -0
- package/dist/core/normalize.d.ts +2 -0
- package/dist/core/normalize.d.ts.map +1 -0
- package/dist/core/render.d.ts +10 -0
- package/dist/core/render.d.ts.map +1 -0
- package/dist/core/richtext.d.ts +2 -0
- package/dist/core/richtext.d.ts.map +1 -0
- package/dist/core/template.d.ts +2 -0
- package/dist/core/template.d.ts.map +1 -0
- package/dist/core/types.d.ts +2 -0
- package/dist/core/types.d.ts.map +1 -0
- package/dist/core/variables.d.ts +2 -0
- package/dist/core/variables.d.ts.map +1 -0
- package/dist/hooks/use-autosave.d.ts +14 -0
- package/dist/hooks/use-autosave.d.ts.map +1 -0
- package/dist/hooks/use-email-builder.d.ts +36 -0
- package/dist/hooks/use-email-builder.d.ts.map +1 -0
- package/dist/hooks/use-render-email.d.ts +15 -0
- package/dist/hooks/use-render-email.d.ts.map +1 -0
- package/dist/index.d.ts +25 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +27976 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/utils.d.ts +3 -0
- package/dist/lib/utils.d.ts.map +1 -0
- package/dist/shims/use-sync-external-store.d.ts +4 -0
- package/dist/shims/use-sync-external-store.d.ts.map +1 -0
- package/dist/store/autosave-context.d.ts +13 -0
- package/dist/store/autosave-context.d.ts.map +1 -0
- package/dist/store/create-email-builder-store.d.ts +42 -0
- package/dist/store/create-email-builder-store.d.ts.map +1 -0
- package/dist/store/email-builder-provider.d.ts +14 -0
- package/dist/store/email-builder-provider.d.ts.map +1 -0
- package/dist/store/vanilla-store.d.ts +8 -0
- package/dist/store/vanilla-store.d.ts.map +1 -0
- package/dist/style.css +2 -0
- package/package.json +70 -0
package/README.md
ADDED
|
@@ -0,0 +1,356 @@
|
|
|
1
|
+
# create-email-template
|
|
2
|
+
|
|
3
|
+
Editor visual de plantillas de email por bloques para React: palette, canvas con drag & drop, edición de texto inline enriquecida, panel de propiedades, autoguardado, deshacer y preview en móvil/escritorio.
|
|
4
|
+
|
|
5
|
+
El JSON que produce se renderiza a HTML email-safe con su compañero [`create-email-renderer`](https://www.npmjs.com/package/create-email-renderer) (**sin React**, pensado para backend/edge). Ambas librerías comparten el mismo core, por lo que el preview del editor y el HTML final nunca divergen.
|
|
6
|
+
|
|
7
|
+
## Instalación
|
|
8
|
+
|
|
9
|
+
```sh
|
|
10
|
+
npm install create-email-template
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
Requiere **React `^18 || ^19`** como peer dependency (usa `useSyncExternalStore`, nativo desde React 18).
|
|
14
|
+
|
|
15
|
+
## Uso mínimo
|
|
16
|
+
|
|
17
|
+
```tsx
|
|
18
|
+
import { EmailBuilder, EmailBuilderProvider } from "create-email-template";
|
|
19
|
+
import "create-email-template/style.css"; // ¡necesario! Estilos autocontenidos
|
|
20
|
+
|
|
21
|
+
export function EditorPage() {
|
|
22
|
+
return (
|
|
23
|
+
<EmailBuilderProvider>
|
|
24
|
+
<EmailBuilder />
|
|
25
|
+
</EmailBuilderProvider>
|
|
26
|
+
);
|
|
27
|
+
}
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
`<EmailBuilder />` renderiza el editor completo (palette + canvas + panel de propiedades). Todo lo demás es configuración opcional.
|
|
31
|
+
|
|
32
|
+
> 📥 **¿Ya creaste una plantilla y quieres volver a editarla?** Ve a [Editar una plantilla existente](#editar-una-plantilla-existente-cargar-del-backend) — se carga con `hydrate()` y el autoguardado sigue guardando sobre esa misma plantilla.
|
|
33
|
+
|
|
34
|
+
---
|
|
35
|
+
|
|
36
|
+
## `<EmailBuilderProvider>` — configuración completa
|
|
37
|
+
|
|
38
|
+
```tsx
|
|
39
|
+
<EmailBuilderProvider
|
|
40
|
+
config={{ /* opciones del builder (todas opcionales) */ }}
|
|
41
|
+
uploadImage={fnUpload} // opcional: subida de imágenes
|
|
42
|
+
autosave={{ /* opcional: autoguardado */ }}
|
|
43
|
+
>
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
### `config`
|
|
47
|
+
|
|
48
|
+
| Opción | Tipo | Default | Qué hace |
|
|
49
|
+
|---|---|---|---|
|
|
50
|
+
| `blockDefaults` | `Partial<Record<Bloque, Partial<Props>>>` | — | Valores iniciales por tipo de bloque. Se aplican **al arrastrar** el bloque al canvas. |
|
|
51
|
+
| `blockLibrary` | `BlockDefinition[]` | `DEFAULT_BLOCK_LIBRARY` (12 bloques) | Qué bloques aparecen en la palette y con qué defaultProps. |
|
|
52
|
+
| `palette` | `Partial<EmailPalette>` | `DEFAULT_PALETTE` | Colores base del tema: `INK` (texto), `DARK` (fondos oscuros), `GOLD` (acentos), `CREAM_DIM` (texto secundario). |
|
|
53
|
+
| `defaultSettings` | `Partial<EmailSettings>` | `DEFAULT_SETTINGS` | Fondo de página, grosor y redondeo del borde de la tarjeta. |
|
|
54
|
+
| `variables` | `EmailVariable[]` | `DEFAULT_VARIABLES` | Etiquetas `{key}` disponibles para el usuario. |
|
|
55
|
+
| `variableSections` | `EmailVariableSection[]` | agrupación única | Agrupa las variables por categoría en el dialog "Etiquetas disponibles". |
|
|
56
|
+
| `sampleContext` | `EmailContext` | `SAMPLE_CONTEXT` | Datos de ejemplo: al previsualizar, `{firstName}` se ve como el valor de aquí. |
|
|
57
|
+
| `labels` | `Partial<EmailBuilderLabels>` | `DEFAULT_LABELS` (español) | Textos de la UI del editor (i18n). |
|
|
58
|
+
| `historyLimit` | `number` | `50` | Profundidad máxima del historial de deshacer. |
|
|
59
|
+
|
|
60
|
+
#### Ejemplo real de `config`
|
|
61
|
+
|
|
62
|
+
```tsx
|
|
63
|
+
<EmailBuilderProvider
|
|
64
|
+
config={{
|
|
65
|
+
// Al arrastrar un header, nace con la marca y el logo ya puestos
|
|
66
|
+
blockDefaults: {
|
|
67
|
+
header: { brandName: "Mi Marca", logoUrl: "https://mi-cdn.com/logo.png" },
|
|
68
|
+
text: { text: "Hola {firstName}, gracias por ser parte de nuestra comunidad." },
|
|
69
|
+
button: { label: "Quiero mi cupo", href: "{registerUrl}", backgroundColor: "#d7b227" },
|
|
70
|
+
footer: { brandName: "Mi Marca" },
|
|
71
|
+
},
|
|
72
|
+
|
|
73
|
+
// Personaliza el tema completo
|
|
74
|
+
palette: { INK: "#221d15", DARK: "#0d0b08", GOLD: "#d7b227", CREAM_DIM: "#b8ae9d" },
|
|
75
|
+
defaultSettings: { pageBackground: "#f5f1e8", cardBorderWidth: 1, cardBorderRadius: 4 },
|
|
76
|
+
|
|
77
|
+
// Contexto de ejemplo para el preview de variables
|
|
78
|
+
sampleContext: {
|
|
79
|
+
firstName: "Danny",
|
|
80
|
+
companyName: "Mi Empresa",
|
|
81
|
+
registerUrl: "https://mi-sitio.com/registro",
|
|
82
|
+
unsubscribeUrl: "https://mi-sitio.com/baja",
|
|
83
|
+
},
|
|
84
|
+
|
|
85
|
+
// Solo estas variables aparecen en el editor (si lo omites: todas las defaults)
|
|
86
|
+
variables: [
|
|
87
|
+
{ key: "firstName", label: "Nombre" },
|
|
88
|
+
{ key: "email", label: "Correo electrónico" },
|
|
89
|
+
{ key: "registerUrl", label: "URL de registro" },
|
|
90
|
+
],
|
|
91
|
+
|
|
92
|
+
// Más undo (cada snapshot es del estado completo de bloques)
|
|
93
|
+
historyLimit: 100,
|
|
94
|
+
}}
|
|
95
|
+
>
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
#### Bloques disponibles y sus props
|
|
99
|
+
|
|
100
|
+
Todos los bloques aceptan `align` ("left" | "center" | "right"), `backgroundColor`, `paddingY` y `paddingX`.
|
|
101
|
+
|
|
102
|
+
| Tipo | Props específicas |
|
|
103
|
+
|---|---|
|
|
104
|
+
| `header` | `brandName`, `tagline?`, `logoUrl?` |
|
|
105
|
+
| `hero` | `title`, `subtitle?`, `imageUrl?` |
|
|
106
|
+
| `heading` | `text`, `size?`, `color?` |
|
|
107
|
+
| `text` | `text`, `size?`, `color?` |
|
|
108
|
+
| `list` | `items: string[]`, `icon?` ("✓", "•", "→"…) |
|
|
109
|
+
| `button` | `label`, `href` (acepta variables), `backgroundColor?`, `color?`, `blockBackgroundColor?` |
|
|
110
|
+
| `image` | `src`, `alt?`, `width?`, `href?` (si hay `href`, la imagen es clicable) |
|
|
111
|
+
| `quote` | `text`, `author?`, `borderColor?`, `borderWidth?`, `marginLeft?` |
|
|
112
|
+
| `columns` | `columns: { id: string; text: string }[]` |
|
|
113
|
+
| `divider` | `color?`, `height?` |
|
|
114
|
+
| `spacer` | `height?`, `backgroundColor?` |
|
|
115
|
+
| `footer` | `text?`, `brandName?`, `backgroundColor?` |
|
|
116
|
+
|
|
117
|
+
### `uploadImage`
|
|
118
|
+
|
|
119
|
+
Se invoca cuando el usuario sube una imagen desde el panel de propiedades. Recibe el `File` y debe devolver la **URL pública** donde quedó alojada (S3, R2, tu API…):
|
|
120
|
+
|
|
121
|
+
```tsx
|
|
122
|
+
uploadImage={async (file: File) => {
|
|
123
|
+
const form = new FormData();
|
|
124
|
+
form.append("file", file);
|
|
125
|
+
const res = await fetch("/api/uploads", { method: "POST", body: form });
|
|
126
|
+
const { url } = await res.json();
|
|
127
|
+
return url; // esta URL queda en la prop `src` del bloque image
|
|
128
|
+
}}
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
---
|
|
132
|
+
|
|
133
|
+
## Autoguardado (`autosave`) y guardado manual
|
|
134
|
+
|
|
135
|
+
Opt-in: sin la prop, no hay guardado automático. La librería **no hace network** — `onSave` es tu fetch.
|
|
136
|
+
|
|
137
|
+
```tsx
|
|
138
|
+
import type { AutosavePayload } from "create-email-template";
|
|
139
|
+
|
|
140
|
+
async function onSave(payload: AutosavePayload) {
|
|
141
|
+
const res = await fetch(`/api/templates/${templateId}`, {
|
|
142
|
+
method: "PUT",
|
|
143
|
+
headers: { "Content-Type": "application/json" },
|
|
144
|
+
body: JSON.stringify(payload), // { content, settings } → tu BD
|
|
145
|
+
});
|
|
146
|
+
if (!res.ok) throw new Error("Error al guardar");
|
|
147
|
+
return res.json(); // lo que devuelvas llega a `lastResult` (ej. { json, html })
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
<EmailBuilderProvider
|
|
151
|
+
autosave={{
|
|
152
|
+
onSave, // obligatorio
|
|
153
|
+
intervalMs: 10_000, // default 10s
|
|
154
|
+
enabled: true, // interruptor sin desmontar el provider
|
|
155
|
+
onSaved: (r) => { // opcional: feedback por intento
|
|
156
|
+
if (!r.ok) toast.error("No se pudo guardar");
|
|
157
|
+
},
|
|
158
|
+
}}
|
|
159
|
+
>
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
Cómo funciona el ciclo: cada `intervalMs`, si hay cambios (`dirty`) y no hay un guardado en vuelo, llama a `onSave` y limpia `dirty`. Si el usuario edita durante el guardado, el próximo tick toma los cambios — no se pierde nada.
|
|
163
|
+
|
|
164
|
+
### `useAutosaveStatus()` — estado y botón de guardado manual
|
|
165
|
+
|
|
166
|
+
```tsx
|
|
167
|
+
import { useAutosaveStatus } from "create-email-template";
|
|
168
|
+
|
|
169
|
+
function Toolbar() {
|
|
170
|
+
const { isSaving, lastSavedAt, lastError, lastResult, saveNow } = useAutosaveStatus();
|
|
171
|
+
|
|
172
|
+
return (
|
|
173
|
+
<>
|
|
174
|
+
{/* saveNow() dispara el MISMO onSave, inmediatamente → botón "Guardar" manual */}
|
|
175
|
+
<button onClick={() => saveNow()} disabled={isSaving}>
|
|
176
|
+
{isSaving ? "Guardando…" : "Guardar"}
|
|
177
|
+
</button>
|
|
178
|
+
|
|
179
|
+
{lastSavedAt && <span>Guardado a las {lastSavedAt.toLocaleTimeString()}</span>}
|
|
180
|
+
{lastError && <span className="error">Error al guardar</span>}
|
|
181
|
+
|
|
182
|
+
{/* lastResult = lo que devolvió tu onSave. Ej.: mostrar el HTML real del backend */}
|
|
183
|
+
{lastResult?.html && <iframe srcDoc={lastResult.html} title="Vista del servidor" />}
|
|
184
|
+
</>
|
|
185
|
+
);
|
|
186
|
+
}
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
| Campo | Descripción |
|
|
190
|
+
|---|---|
|
|
191
|
+
| `isSaving` | Hay un guardado en vuelo. |
|
|
192
|
+
| `lastSavedAt` | Fecha del último guardado exitoso. |
|
|
193
|
+
| `lastError` | Error del último intento fallido (se limpia al guardar bien). |
|
|
194
|
+
| `lastResult` | Lo que devolvió tu `onSave` — ej. `{ json, html }` si tu backend renderiza y responde el HTML. |
|
|
195
|
+
| `saveNow()` | Guardado inmediato con los mismos guards: no hace nada si no hay cambios o ya hay uno en vuelo. |
|
|
196
|
+
|
|
197
|
+
¿Solo botón manual, sin autoguardado? `autosave={{ enabled: false, onSave }}` — `saveNow()` sigue funcionando.
|
|
198
|
+
|
|
199
|
+
---
|
|
200
|
+
|
|
201
|
+
## Editar una plantilla existente (cargar del backend)
|
|
202
|
+
|
|
203
|
+
¿Ya creaste una plantilla y quieres volver a editarla? El ciclo completo es: **fetch del JSON → `hydrate()` → el usuario edita → el autoguardado guarda sobre la misma plantilla**. Ejemplo de página completa:
|
|
204
|
+
|
|
205
|
+
```tsx
|
|
206
|
+
import { useEffect, useState } from "react";
|
|
207
|
+
import {
|
|
208
|
+
EmailBuilder,
|
|
209
|
+
EmailBuilderProvider,
|
|
210
|
+
useEmailBuilderStoreInstance,
|
|
211
|
+
type AutosavePayload,
|
|
212
|
+
} from "create-email-template";
|
|
213
|
+
import "create-email-template/style.css";
|
|
214
|
+
|
|
215
|
+
// 1. Página: trae la plantilla y no monta el editor hasta que llegue
|
|
216
|
+
export function EditorPage({ templateId }: { templateId: string }) {
|
|
217
|
+
const [payload, setPayload] = useState<AutosavePayload | null>(null);
|
|
218
|
+
|
|
219
|
+
useEffect(() => {
|
|
220
|
+
fetch(`/api/templates/${templateId}`)
|
|
221
|
+
.then((r) => r.json())
|
|
222
|
+
.then((template) => setPayload(template.payload)); // { content, settings }
|
|
223
|
+
}, [templateId]);
|
|
224
|
+
|
|
225
|
+
if (!payload) return <p>Cargando plantilla…</p>;
|
|
226
|
+
|
|
227
|
+
return (
|
|
228
|
+
<EmailBuilderProvider
|
|
229
|
+
config={miConfig}
|
|
230
|
+
autosave={{
|
|
231
|
+
// 4. El autoguardado ahora actualiza ESA MISMA plantilla
|
|
232
|
+
onSave: (data) =>
|
|
233
|
+
fetch(`/api/templates/${templateId}`, {
|
|
234
|
+
method: "PUT",
|
|
235
|
+
headers: { "Content-Type": "application/json" },
|
|
236
|
+
body: JSON.stringify(data),
|
|
237
|
+
}),
|
|
238
|
+
}}
|
|
239
|
+
>
|
|
240
|
+
{/* 2. Componente puente: inyecta el payload en el store */}
|
|
241
|
+
<TemplateLoader payload={payload} />
|
|
242
|
+
{/* 3. El editor arranca ya con los bloques de la plantilla */}
|
|
243
|
+
<EmailBuilder />
|
|
244
|
+
</EmailBuilderProvider>
|
|
245
|
+
);
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
// Debe vivir DENTRO del provider (usa el store del builder). No renderiza nada.
|
|
249
|
+
function TemplateLoader({ payload }: { payload: AutosavePayload }) {
|
|
250
|
+
const store = useEmailBuilderStoreInstance();
|
|
251
|
+
|
|
252
|
+
useEffect(() => {
|
|
253
|
+
store.getState().hydrate({
|
|
254
|
+
blocks: payload.content,
|
|
255
|
+
settings: payload.settings, // opcional
|
|
256
|
+
});
|
|
257
|
+
}, [payload, store]);
|
|
258
|
+
|
|
259
|
+
return null;
|
|
260
|
+
}
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
Qué hace `hydrate({ blocks, settings? })`:
|
|
264
|
+
|
|
265
|
+
- **Normaliza el payload**: props faltantes se completan con los defaults actuales, tipos desconocidos se descartan — plantillas guardadas con versiones anteriores del esquema cargan sin romperse.
|
|
266
|
+
- **Reinicia el historial de deshacer**: el estado cargado es la base nueva (el undo anterior se descarta).
|
|
267
|
+
- Deja `dirty: false`: el autoguardado **no dispara** hasta que el usuario edite de verdad.
|
|
268
|
+
- Los `id` de los bloques se conservan, así que el guardado actualiza en lugar de duplicar.
|
|
269
|
+
|
|
270
|
+
> 💡 Cargar antes de montar el editor (el `if (!payload)` del ejemplo) no es obligatorio, pero evita que el usuario edite un canvas vacío y su trabajo se sobrescriba cuando llegue el fetch.
|
|
271
|
+
|
|
272
|
+
---
|
|
273
|
+
|
|
274
|
+
## Deshacer
|
|
275
|
+
|
|
276
|
+
Undo **lineal** (solo hacia atrás, nunca adelante), en memoria:
|
|
277
|
+
|
|
278
|
+
- Cada acción (añadir/eliminar/reordenar/duplicar bloques, editar propiedades y ajustes) registra un snapshot. Tope: `historyLimit` (50).
|
|
279
|
+
- Las escrituras continuas se **agrupan**: Ctrl+Z deshace la frase escrita, no tecla por tecla.
|
|
280
|
+
- **Ctrl/Cmd+Z ya funciona** dentro de `<EmailBuilder />`. En un campo de texto editable se respeta el undo nativo.
|
|
281
|
+
- Deshacer marca `dirty: true` → el autoguardado persiste el estado deshecho en el siguiente tick.
|
|
282
|
+
- Al `hydrate()` el historial se vacía (el estado cargado es la nueva base).
|
|
283
|
+
|
|
284
|
+
Para tu propia UI de undo:
|
|
285
|
+
|
|
286
|
+
```tsx
|
|
287
|
+
import { useEmailBuilderStore } from "create-email-template";
|
|
288
|
+
|
|
289
|
+
function UndoButton() {
|
|
290
|
+
const canUndo = useEmailBuilderStore((s) => s.past.length > 0);
|
|
291
|
+
const undo = useEmailBuilderStore((s) => s.undo);
|
|
292
|
+
return <button onClick={() => undo()} disabled={!canUndo}>Deshacer</button>;
|
|
293
|
+
}
|
|
294
|
+
```
|
|
295
|
+
|
|
296
|
+
---
|
|
297
|
+
|
|
298
|
+
## `useRenderEmail()` — payload y render de preview
|
|
299
|
+
|
|
300
|
+
```tsx
|
|
301
|
+
import { useRenderEmail } from "create-email-template";
|
|
302
|
+
|
|
303
|
+
function Acciones() {
|
|
304
|
+
const { getPayload, renderHtml, getHtml, resolve, html, isPending } = useRenderEmail();
|
|
305
|
+
|
|
306
|
+
const guardar = () => fetch("/api/templates", { method: "POST", body: JSON.stringify(getPayload()) });
|
|
307
|
+
const preview = () => renderHtml({ subject: "Hola {firstName}" }); // html se actualiza
|
|
308
|
+
|
|
309
|
+
return <button onClick={preview} disabled={isPending}>Refrescar preview</button>;
|
|
310
|
+
}
|
|
311
|
+
```
|
|
312
|
+
|
|
313
|
+
| Miembro | Descripción |
|
|
314
|
+
|---|---|
|
|
315
|
+
| `getPayload()` | `{ content: EmailBlock[], settings }` — el JSON para persistir. |
|
|
316
|
+
| `renderHtml(opts?)` | Renderiza el preview con react-email (async). Opciones: `{ subject?, context? }`. |
|
|
317
|
+
| `getHtml(opts?)` | Igual que `renderHtml` pero cacheado: sin opciones devuelve el HTML ya generado. |
|
|
318
|
+
| `resolve(text, ctx?)` | Reemplaza `{etiquetas}` en un texto con el `sampleContext` (o el que pases). |
|
|
319
|
+
| `html` / `isPending` / `setHtml` | Estado reactivo del último render. |
|
|
320
|
+
|
|
321
|
+
---
|
|
322
|
+
|
|
323
|
+
## `useEmailBuilderStore(selector)` — estado y acciones
|
|
324
|
+
|
|
325
|
+
Suscripción selectiva (re-renderiza solo lo que cambia). Requiere el provider.
|
|
326
|
+
|
|
327
|
+
```tsx
|
|
328
|
+
import { useEmailBuilderStore } from "create-email-template";
|
|
329
|
+
|
|
330
|
+
const blocks = useEmailBuilderStore((s) => s.blocks); // bloques actuales
|
|
331
|
+
const dirty = useEmailBuilderStore((s) => s.dirty); // ¿hay cambios sin guardar?
|
|
332
|
+
const canUndo = useEmailBuilderStore((s) => s.past.length > 0);
|
|
333
|
+
const undo = useEmailBuilderStore((s) => s.undo);
|
|
334
|
+
const addBlock = useEmailBuilderStore((s) => s.addBlock);
|
|
335
|
+
|
|
336
|
+
addBlock("text"); // añade un bloque de texto al final (con tus blockDefaults)
|
|
337
|
+
addBlock("button", 0); // …o en una posición específica
|
|
338
|
+
```
|
|
339
|
+
|
|
340
|
+
**Estado:** `blocks`, `selectedId`, `settings`, `dirty`, `propertiesOpen`, `past` (pila de undo).
|
|
341
|
+
|
|
342
|
+
**Acciones:** `addBlock(type, index?)` · `removeBlock(id)` · `duplicateBlock(id)` · `updateBlockProps(id, props)` · `reorder(activeIndex, overIndex)` · `setSettings(patch)` · `select(id)` · `setPropertiesOpen(open)` · `hydrate({ blocks, settings? })` · `undo()` · `markSaved()` · `getPayload()`.
|
|
343
|
+
|
|
344
|
+
¿Fuera de React? `createEmailBuilderStore(config)` crea el mismo store vanilla (`getState`/`setState`/`subscribe`) sin dependencias.
|
|
345
|
+
|
|
346
|
+
---
|
|
347
|
+
|
|
348
|
+
## Notas
|
|
349
|
+
|
|
350
|
+
- **Estilos**: importa `create-email-template/style.css` una vez. Todo va bajo la clase `.ter-theme` con clases `ter-*` — no colisiona con tu Tailwind/CSS.
|
|
351
|
+
- **Componentes sueltos**: también se exportan `BlockPalette`, `Canvas`, `PropertiesPanel`, `EditableBlockRenderer`, `InlineTextEditor`, `SelectionToolbar`, `VariablesInfoDialog` para armar un editor a medida.
|
|
352
|
+
- **Demo completa**: `apps/vite-test` del [monorepo](https://github.com/asmel2020/creator-email-templates) — editor con config de muestra, autoguardado, undo y dialog comparando el render de React vs. el HTML del backend.
|
|
353
|
+
|
|
354
|
+
## Licencia
|
|
355
|
+
|
|
356
|
+
MIT
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"block-palette.d.ts","sourceRoot":"","sources":["../../../src/components/builder/block-palette.tsx"],"names":[],"mappings":"AAuCA,eAAO,MAAM,YAAY,GAAI,aAAqB;IAAE,OAAO,CAAC,EAAE,OAAO,CAAA;CAAE,gCAatE,CAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"canvas.d.ts","sourceRoot":"","sources":["../../../src/components/builder/canvas.tsx"],"names":[],"mappings":"AA6BA,eAAO,MAAM,cAAc,GAAI,cAE5B;IACD,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB,gCA+NA,CAAC"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { type EmailBlock } from "../../core/types";
|
|
2
|
+
interface Props {
|
|
3
|
+
block: EmailBlock;
|
|
4
|
+
}
|
|
5
|
+
export declare const EditableBlockRenderer: ({ block }: Props) => import("react").JSX.Element | null;
|
|
6
|
+
export {};
|
|
7
|
+
//# sourceMappingURL=editable-block-renderer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"editable-block-renderer.d.ts","sourceRoot":"","sources":["../../../src/components/builder/editable-block-renderer.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,UAAU,EAAE,MAAM,kBAAkB,CAAA;AAUlD,UAAU,KAAK;IACb,KAAK,EAAE,UAAU,CAAA;CAClB;AAED,eAAO,MAAM,qBAAqB,GAAI,WAAW,KAAK,uCAycrD,CAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"email-builder.d.ts","sourceRoot":"","sources":["../../../src/components/builder/email-builder.tsx"],"names":[],"mappings":"AAgCA,MAAM,WAAW,iBAAiB;IAChC,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAED,eAAO,MAAM,YAAY,GAAI,eAAe,iBAAiB,gCA6M5D,CAAA"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
interface Props {
|
|
3
|
+
value: string;
|
|
4
|
+
onChange: (next: string) => void;
|
|
5
|
+
rich?: boolean;
|
|
6
|
+
className?: string;
|
|
7
|
+
style?: React.CSSProperties;
|
|
8
|
+
onEnter?: () => void;
|
|
9
|
+
onDeleteWhenEmpty?: () => void;
|
|
10
|
+
autoFocus?: boolean;
|
|
11
|
+
placeholder?: string;
|
|
12
|
+
}
|
|
13
|
+
export declare const InlineTextEditor: ({ value, onChange, rich, className, style, onEnter, onDeleteWhenEmpty, autoFocus, placeholder, }: Props) => React.JSX.Element;
|
|
14
|
+
export {};
|
|
15
|
+
//# sourceMappingURL=inline-text-editor.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"inline-text-editor.d.ts","sourceRoot":"","sources":["../../../src/components/builder/inline-text-editor.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAmD,MAAM,OAAO,CAAC;AAiDxE,UAAU,KAAK;IACb,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACjC,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,KAAK,CAAC,aAAa,CAAC;IAC5B,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,iBAAiB,CAAC,EAAE,MAAM,IAAI,CAAC;IAC/B,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,eAAO,MAAM,gBAAgB,GAAI,kGAU9B,KAAK,sBA+RP,CAAC"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { type EmailBlock } from "../../core/types";
|
|
3
|
+
interface Props {
|
|
4
|
+
block: EmailBlock;
|
|
5
|
+
}
|
|
6
|
+
export declare const PropertiesPanel: ({ block }: Props) => React.JSX.Element;
|
|
7
|
+
export {};
|
|
8
|
+
//# sourceMappingURL=properties-panel.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"properties-panel.d.ts","sourceRoot":"","sources":["../../../src/components/builder/properties-panel.tsx"],"names":[],"mappings":"AAEA,OAAO,KAA2B,MAAM,OAAO,CAAA;AAI/C,OAAO,EAAE,KAAK,UAAU,EAAc,MAAM,kBAAkB,CAAA;AAyK9D,UAAU,KAAK;IACb,KAAK,EAAE,UAAU,CAAA;CAClB;AAYD,eAAO,MAAM,eAAe,GAAI,WAAW,KAAK,sBA0U/C,CAAA"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
interface Props {
|
|
3
|
+
style: React.CSSProperties;
|
|
4
|
+
onFormat: (command: string, value?: string) => void;
|
|
5
|
+
innerRef?: React.Ref<HTMLDivElement>;
|
|
6
|
+
selectionSize: number | null;
|
|
7
|
+
selectionMaxSize: number;
|
|
8
|
+
selectionColor: string | null;
|
|
9
|
+
}
|
|
10
|
+
export declare const SelectionToolbar: ({ style, onFormat, innerRef, selectionSize, selectionMaxSize, selectionColor, }: Props) => React.JSX.Element;
|
|
11
|
+
export {};
|
|
12
|
+
//# sourceMappingURL=selection-toolbar.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"selection-toolbar.d.ts","sourceRoot":"","sources":["../../../src/components/builder/selection-toolbar.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAmB,MAAM,OAAO,CAAC;AAcxC,UAAU,KAAK;IACb,KAAK,EAAE,KAAK,CAAC,aAAa,CAAC;IAC3B,QAAQ,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;IACpD,QAAQ,CAAC,EAAE,KAAK,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC;IACrC,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,gBAAgB,EAAE,MAAM,CAAC;IACzB,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;CAC/B;AAiBD,eAAO,MAAM,gBAAgB,GAAI,iFAO9B,KAAK,sBAwNP,CAAC"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { type EmailBlock } from "../../core/types";
|
|
2
|
+
interface Props {
|
|
3
|
+
block: EmailBlock;
|
|
4
|
+
index: number;
|
|
5
|
+
isFirst?: boolean;
|
|
6
|
+
cardBorderRadius?: number;
|
|
7
|
+
}
|
|
8
|
+
export declare const SortableItem: ({ block, index, isFirst, cardBorderRadius, }: Props) => import("react").JSX.Element;
|
|
9
|
+
export {};
|
|
10
|
+
//# sourceMappingURL=sortable-item.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sortable-item.d.ts","sourceRoot":"","sources":["../../../src/components/builder/sortable-item.tsx"],"names":[],"mappings":"AAIA,OAAO,EAAE,KAAK,UAAU,EAAE,MAAM,kBAAkB,CAAA;AAgBlD,UAAU,KAAK;IACb,KAAK,EAAE,UAAU,CAAA;IACjB,KAAK,EAAE,MAAM,CAAA;IACb,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,gBAAgB,CAAC,EAAE,MAAM,CAAA;CAC1B;AAED,eAAO,MAAM,YAAY,GAAI,8CAK1B,KAAK,gCAyGP,CAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"variables-info-dialog.d.ts","sourceRoot":"","sources":["../../../src/components/builder/variables-info-dialog.tsx"],"names":[],"mappings":"AAaA,UAAU,KAAK;IACb,IAAI,EAAE,OAAO,CAAA;IACb,YAAY,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAA;CACtC;AA2GD,eAAO,MAAM,mBAAmB,GAAI,wBAAwB,KAAK,gCA4BhE,CAAA"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { Button as ButtonPrimitive } from "@base-ui/react/button";
|
|
2
|
+
type ButtonVariant = "default" | "outline" | "secondary" | "ghost" | "destructive" | "link";
|
|
3
|
+
type ButtonSize = "default" | "xs" | "sm" | "lg" | "icon" | "icon-xs" | "icon-sm" | "icon-lg";
|
|
4
|
+
export declare function Button({ className, variant, size, ...props }: ButtonPrimitive.Props & {
|
|
5
|
+
variant?: ButtonVariant;
|
|
6
|
+
size?: ButtonSize;
|
|
7
|
+
}): import("react").JSX.Element;
|
|
8
|
+
export declare const buttonVariants: () => string;
|
|
9
|
+
export {};
|
|
10
|
+
//# sourceMappingURL=button.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"button.d.ts","sourceRoot":"","sources":["../../../src/components/ui/button.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,IAAI,eAAe,EAAE,MAAM,uBAAuB,CAAA;AAIjE,KAAK,aAAa,GAAG,SAAS,GAAG,SAAS,GAAG,WAAW,GAAG,OAAO,GAAG,aAAa,GAAG,MAAM,CAAA;AAC3F,KAAK,UAAU,GAAG,SAAS,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,MAAM,GAAG,SAAS,GAAG,SAAS,GAAG,SAAS,CAAA;AAsB7F,wBAAgB,MAAM,CAAC,EACrB,SAAS,EACT,OAAmB,EACnB,IAAgB,EAChB,GAAG,KAAK,EACT,EAAE,eAAe,CAAC,KAAK,GAAG;IAAE,OAAO,CAAC,EAAE,aAAa,CAAC;IAAC,IAAI,CAAC,EAAE,UAAU,CAAA;CAAE,+BAQxE;AAED,eAAO,MAAM,cAAc,cAAW,CAAA"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { Dialog as DialogPrimitive } from "@base-ui/react/dialog";
|
|
3
|
+
declare function Dialog({ ...props }: DialogPrimitive.Root.Props): React.JSX.Element;
|
|
4
|
+
declare function DialogTrigger({ ...props }: DialogPrimitive.Trigger.Props): React.JSX.Element;
|
|
5
|
+
declare function DialogPortal({ ...props }: DialogPrimitive.Portal.Props): React.JSX.Element;
|
|
6
|
+
declare function DialogClose({ ...props }: DialogPrimitive.Close.Props): React.JSX.Element;
|
|
7
|
+
declare function DialogOverlay({ className, ...props }: DialogPrimitive.Backdrop.Props): React.JSX.Element;
|
|
8
|
+
declare function DialogContent({ className, children, showCloseButton, ...props }: DialogPrimitive.Popup.Props & {
|
|
9
|
+
showCloseButton?: boolean;
|
|
10
|
+
}): React.JSX.Element;
|
|
11
|
+
declare function DialogHeader({ className, ...props }: React.ComponentProps<"div">): React.JSX.Element;
|
|
12
|
+
declare function DialogFooter({ className, showCloseButton, children, ...props }: React.ComponentProps<"div"> & {
|
|
13
|
+
showCloseButton?: boolean;
|
|
14
|
+
}): React.JSX.Element;
|
|
15
|
+
declare function DialogTitle({ className, ...props }: DialogPrimitive.Title.Props): React.JSX.Element;
|
|
16
|
+
declare function DialogDescription({ className, ...props }: DialogPrimitive.Description.Props): React.JSX.Element;
|
|
17
|
+
export { Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogOverlay, DialogPortal, DialogTitle, DialogTrigger, };
|
|
18
|
+
//# sourceMappingURL=dialog.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dialog.d.ts","sourceRoot":"","sources":["../../../src/components/ui/dialog.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAC9B,OAAO,EAAE,MAAM,IAAI,eAAe,EAAE,MAAM,uBAAuB,CAAA;AAMjE,iBAAS,MAAM,CAAC,EAAE,GAAG,KAAK,EAAE,EAAE,eAAe,CAAC,IAAI,CAAC,KAAK,qBAEvD;AAED,iBAAS,aAAa,CAAC,EAAE,GAAG,KAAK,EAAE,EAAE,eAAe,CAAC,OAAO,CAAC,KAAK,qBAEjE;AAED,iBAAS,YAAY,CAAC,EAAE,GAAG,KAAK,EAAE,EAAE,eAAe,CAAC,MAAM,CAAC,KAAK,qBAE/D;AAED,iBAAS,WAAW,CAAC,EAAE,GAAG,KAAK,EAAE,EAAE,eAAe,CAAC,KAAK,CAAC,KAAK,qBAE7D;AAED,iBAAS,aAAa,CAAC,EACrB,SAAS,EACT,GAAG,KAAK,EACT,EAAE,eAAe,CAAC,QAAQ,CAAC,KAAK,qBAQhC;AAED,iBAAS,aAAa,CAAC,EACrB,SAAS,EACT,QAAQ,EACR,eAAsB,EACtB,GAAG,KAAK,EACT,EAAE,eAAe,CAAC,KAAK,CAAC,KAAK,GAAG;IAC/B,eAAe,CAAC,EAAE,OAAO,CAAA;CAC1B,qBA6BA;AAED,iBAAS,YAAY,CAAC,EAAE,SAAS,EAAE,GAAG,KAAK,EAAE,EAAE,KAAK,CAAC,cAAc,CAAC,KAAK,CAAC,qBAQzE;AAED,iBAAS,YAAY,CAAC,EACpB,SAAS,EACT,eAAuB,EACvB,QAAQ,EACR,GAAG,KAAK,EACT,EAAE,KAAK,CAAC,cAAc,CAAC,KAAK,CAAC,GAAG;IAC/B,eAAe,CAAC,EAAE,OAAO,CAAA;CAC1B,qBAeA;AAED,iBAAS,WAAW,CAAC,EAAE,SAAS,EAAE,GAAG,KAAK,EAAE,EAAE,eAAe,CAAC,KAAK,CAAC,KAAK,qBAWxE;AAED,iBAAS,iBAAiB,CAAC,EACzB,SAAS,EACT,GAAG,KAAK,EACT,EAAE,eAAe,CAAC,WAAW,CAAC,KAAK,qBAQnC;AAED,OAAO,EACL,MAAM,EACN,WAAW,EACX,aAAa,EACb,iBAAiB,EACjB,YAAY,EACZ,YAAY,EACZ,aAAa,EACb,YAAY,EACZ,WAAW,EACX,aAAa,GACd,CAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"input.d.ts","sourceRoot":"","sources":["../../../src/components/ui/input.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAK9B,iBAAS,KAAK,CAAC,EAAE,SAAS,EAAE,IAAI,EAAE,GAAG,KAAK,EAAE,EAAE,KAAK,CAAC,cAAc,CAAC,OAAO,CAAC,qBAS1E;AAED,OAAO,EAAE,KAAK,EAAE,CAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"label.d.ts","sourceRoot":"","sources":["../../../src/components/ui/label.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAI9B,iBAAS,KAAK,CAAC,EAAE,SAAS,EAAE,GAAG,KAAK,EAAE,EAAE,KAAK,CAAC,cAAc,CAAC,OAAO,CAAC,qBAQpE;AAED,OAAO,EAAE,KAAK,EAAE,CAAA"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { Dialog as SheetPrimitive } from "@base-ui/react/dialog";
|
|
3
|
+
declare function Sheet({ ...props }: SheetPrimitive.Root.Props): React.JSX.Element;
|
|
4
|
+
declare function SheetTrigger({ ...props }: SheetPrimitive.Trigger.Props): React.JSX.Element;
|
|
5
|
+
declare function SheetClose({ ...props }: SheetPrimitive.Close.Props): React.JSX.Element;
|
|
6
|
+
declare function SheetContent({ className, children, side, showCloseButton, ...props }: SheetPrimitive.Popup.Props & {
|
|
7
|
+
side?: "top" | "right" | "bottom" | "left";
|
|
8
|
+
showCloseButton?: boolean;
|
|
9
|
+
}): React.JSX.Element;
|
|
10
|
+
declare function SheetHeader({ className, ...props }: React.ComponentProps<"div">): React.JSX.Element;
|
|
11
|
+
declare function SheetFooter({ className, ...props }: React.ComponentProps<"div">): React.JSX.Element;
|
|
12
|
+
declare function SheetTitle({ className, ...props }: SheetPrimitive.Title.Props): React.JSX.Element;
|
|
13
|
+
declare function SheetDescription({ className, ...props }: SheetPrimitive.Description.Props): React.JSX.Element;
|
|
14
|
+
export { Sheet, SheetTrigger, SheetClose, SheetContent, SheetHeader, SheetFooter, SheetTitle, SheetDescription, };
|
|
15
|
+
//# sourceMappingURL=sheet.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sheet.d.ts","sourceRoot":"","sources":["../../../src/components/ui/sheet.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAC9B,OAAO,EAAE,MAAM,IAAI,cAAc,EAAE,MAAM,uBAAuB,CAAA;AAMhE,iBAAS,KAAK,CAAC,EAAE,GAAG,KAAK,EAAE,EAAE,cAAc,CAAC,IAAI,CAAC,KAAK,qBAErD;AAED,iBAAS,YAAY,CAAC,EAAE,GAAG,KAAK,EAAE,EAAE,cAAc,CAAC,OAAO,CAAC,KAAK,qBAE/D;AAED,iBAAS,UAAU,CAAC,EAAE,GAAG,KAAK,EAAE,EAAE,cAAc,CAAC,KAAK,CAAC,KAAK,qBAE3D;AAgBD,iBAAS,YAAY,CAAC,EACpB,SAAS,EACT,QAAQ,EACR,IAAc,EACd,eAAsB,EACtB,GAAG,KAAK,EACT,EAAE,cAAc,CAAC,KAAK,CAAC,KAAK,GAAG;IAC9B,IAAI,CAAC,EAAE,KAAK,GAAG,OAAO,GAAG,QAAQ,GAAG,MAAM,CAAA;IAC1C,eAAe,CAAC,EAAE,OAAO,CAAA;CAC1B,qBA8BA;AAED,iBAAS,WAAW,CAAC,EAAE,SAAS,EAAE,GAAG,KAAK,EAAE,EAAE,KAAK,CAAC,cAAc,CAAC,KAAK,CAAC,qBAQxE;AAED,iBAAS,WAAW,CAAC,EAAE,SAAS,EAAE,GAAG,KAAK,EAAE,EAAE,KAAK,CAAC,cAAc,CAAC,KAAK,CAAC,qBAQxE;AAED,iBAAS,UAAU,CAAC,EAAE,SAAS,EAAE,GAAG,KAAK,EAAE,EAAE,cAAc,CAAC,KAAK,CAAC,KAAK,qBAWtE;AAED,iBAAS,gBAAgB,CAAC,EACxB,SAAS,EACT,GAAG,KAAK,EACT,EAAE,cAAc,CAAC,WAAW,CAAC,KAAK,qBAQlC;AAED,OAAO,EACL,KAAK,EACL,YAAY,EACZ,UAAU,EACV,YAAY,EACZ,WAAW,EACX,WAAW,EACX,UAAU,EACV,gBAAgB,GACjB,CAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"defaults.d.ts","sourceRoot":"","sources":["../../src/config/defaults.ts"],"names":[],"mappings":"AAgBA,OAAO,KAAK,EAEV,kBAAkB,EAClB,0BAA0B,EAC3B,MAAM,SAAS,CAAC;AAejB,eAAO,MAAM,yBAAyB,GACpC,SAAS,kBAAkB,KAC1B,0BAyCF,CAAC"}
|