@mjml-agent-editor/editor 0.3.0 → 0.4.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 +2 -1
- package/dist/canvas.cjs +156 -143
- package/dist/canvas.cjs.map +1 -1
- package/dist/canvas.js +157 -144
- package/dist/canvas.js.map +1 -1
- package/dist/{chunk-K7KQC7LX.js → chunk-PXFZVLNQ.js} +1 -1
- package/dist/chunk-PXFZVLNQ.js.map +1 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +4 -1
- package/dist/index.d.ts +4 -1
- package/dist/index.js +1 -1
- package/package.json +1 -1
- package/dist/chunk-K7KQC7LX.js.map +0 -1
package/dist/canvas.js
CHANGED
|
@@ -16,7 +16,7 @@ import {
|
|
|
16
16
|
useDocumentStore,
|
|
17
17
|
useImageLibrary,
|
|
18
18
|
useLabels
|
|
19
|
-
} from "./chunk-
|
|
19
|
+
} from "./chunk-PXFZVLNQ.js";
|
|
20
20
|
import {
|
|
21
21
|
Button,
|
|
22
22
|
Input,
|
|
@@ -218,7 +218,8 @@ function ImagePicker({
|
|
|
218
218
|
const accepts = (file) => matchesAccept(file, library?.accept);
|
|
219
219
|
const full = quota !== void 0 && quota.used >= quota.limit;
|
|
220
220
|
const upload = async (files) => {
|
|
221
|
-
|
|
221
|
+
const send = library?.upload?.bind(library);
|
|
222
|
+
if (!send || !files?.length) return;
|
|
222
223
|
const mine = session.current;
|
|
223
224
|
const list = Array.from(files).filter((f) => {
|
|
224
225
|
if (accepts(f)) return true;
|
|
@@ -227,7 +228,7 @@ function ImagePicker({
|
|
|
227
228
|
});
|
|
228
229
|
if (!list.length) return;
|
|
229
230
|
setPending((n) => n + list.length);
|
|
230
|
-
const results = await Promise.allSettled(list.map(async (f) =>
|
|
231
|
+
const results = await Promise.allSettled(list.map(async (f) => send(f)));
|
|
231
232
|
setPending((n) => n - list.length);
|
|
232
233
|
for (const r of results) {
|
|
233
234
|
if (r.status === "rejected") {
|
|
@@ -262,155 +263,163 @@ function ImagePicker({
|
|
|
262
263
|
setDragging(false);
|
|
263
264
|
if (!full) void upload(e.dataTransfer.files);
|
|
264
265
|
};
|
|
265
|
-
return /* @__PURE__ */ jsx2(Dialog, { open, onOpenChange: (next) => !next && onClose(), children: /* @__PURE__ */ jsxs(
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
"
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
onDrop,
|
|
280
|
-
className: cn(
|
|
281
|
-
"flex flex-col items-center justify-center gap-3 rounded-xl border-2 border-dashed px-6 py-8 text-center transition-colors",
|
|
282
|
-
dragging ? "border-brand bg-brand-soft" : "border-panel-border bg-panel-elevated"
|
|
283
|
-
),
|
|
284
|
-
children: [
|
|
285
|
-
/* @__PURE__ */ jsx2(ImageUp, { className: "size-8 text-panel-muted-fg" }),
|
|
286
|
-
/* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center justify-center gap-2 text-sm text-panel-muted-fg", children: [
|
|
287
|
-
/* @__PURE__ */ jsx2("span", { children: labels.imagePickerDrop }),
|
|
288
|
-
/* @__PURE__ */ jsx2(
|
|
289
|
-
Button,
|
|
290
|
-
{
|
|
291
|
-
type: "button",
|
|
292
|
-
size: "sm",
|
|
293
|
-
className: "bg-brand text-brand-fg hover:bg-brand/90",
|
|
294
|
-
onClick: () => fileInput.current?.click(),
|
|
295
|
-
children: labels.imagePickerBrowse
|
|
296
|
-
}
|
|
297
|
-
)
|
|
298
|
-
] }),
|
|
299
|
-
/* @__PURE__ */ jsx2(
|
|
300
|
-
"input",
|
|
301
|
-
{
|
|
302
|
-
ref: fileInput,
|
|
303
|
-
type: "file",
|
|
304
|
-
accept: library.accept ?? "image/*",
|
|
305
|
-
multiple: true,
|
|
306
|
-
hidden: true,
|
|
307
|
-
onChange: (e) => {
|
|
308
|
-
void upload(e.target.files);
|
|
309
|
-
e.target.value = "";
|
|
310
|
-
}
|
|
311
|
-
}
|
|
312
|
-
)
|
|
313
|
-
]
|
|
314
|
-
}
|
|
315
|
-
)),
|
|
316
|
-
/* @__PURE__ */ jsxs(
|
|
317
|
-
"form",
|
|
318
|
-
{
|
|
319
|
-
className: "flex gap-2",
|
|
320
|
-
onSubmit: (e) => {
|
|
321
|
-
e.preventDefault();
|
|
322
|
-
const value = url.trim();
|
|
323
|
-
if (value) onSelect(value);
|
|
324
|
-
},
|
|
325
|
-
children: [
|
|
326
|
-
/* @__PURE__ */ jsxs("div", { className: "relative flex-1", children: [
|
|
327
|
-
/* @__PURE__ */ jsx2(Link2, { className: "pointer-events-none absolute top-1/2 left-2.5 size-4 -translate-y-1/2 text-panel-muted-fg" }),
|
|
328
|
-
/* @__PURE__ */ jsx2(
|
|
329
|
-
Input,
|
|
330
|
-
{
|
|
331
|
-
value: url,
|
|
332
|
-
onChange: (e) => setUrl(e.target.value),
|
|
333
|
-
placeholder: labels.imagePickerUrlPlaceholder,
|
|
334
|
-
className: "h-9 border-panel-border bg-panel-elevated pl-8 text-panel-fg placeholder:text-panel-muted-fg"
|
|
335
|
-
}
|
|
336
|
-
)
|
|
337
|
-
] }),
|
|
338
|
-
/* @__PURE__ */ jsx2(
|
|
339
|
-
Button,
|
|
340
|
-
{
|
|
341
|
-
type: "submit",
|
|
342
|
-
size: "lg",
|
|
343
|
-
variant: "outline",
|
|
344
|
-
disabled: !url.trim(),
|
|
345
|
-
className: "border-panel-border bg-panel-elevated text-panel-fg hover:bg-panel-hover hover:text-panel-fg",
|
|
346
|
-
children: labels.imagePickerUseUrl
|
|
347
|
-
}
|
|
348
|
-
)
|
|
349
|
-
]
|
|
350
|
-
}
|
|
351
|
-
),
|
|
352
|
-
library && /* @__PURE__ */ jsxs("section", { children: [
|
|
353
|
-
/* @__PURE__ */ jsx2("h3", { className: "mb-2 text-xs font-medium tracking-wide text-panel-muted-fg uppercase", children: labels.imagePickerGallery }),
|
|
354
|
-
loading && images.length === 0 && pending === 0 ? /* @__PURE__ */ jsx2("div", { className: "flex justify-center py-8", children: /* @__PURE__ */ jsx2(Spinner, {}) }) : images.length === 0 && pending === 0 ? /* @__PURE__ */ jsx2("p", { className: "py-6 text-center text-sm text-panel-muted-fg", children: labels.imagePickerEmpty }) : /* @__PURE__ */ jsxs("ul", { className: "grid grid-cols-3 gap-3 sm:grid-cols-4", children: [
|
|
355
|
-
Array.from({ length: pending }, (_, i) => /* @__PURE__ */ jsx2(
|
|
356
|
-
"li",
|
|
266
|
+
return /* @__PURE__ */ jsx2(Dialog, { open, onOpenChange: (next) => !next && onClose(), children: /* @__PURE__ */ jsxs(
|
|
267
|
+
DialogContent,
|
|
268
|
+
{
|
|
269
|
+
onDragOver: (e) => e.preventDefault(),
|
|
270
|
+
onDrop: (e) => e.preventDefault(),
|
|
271
|
+
className: "editor-dark gap-0 overflow-hidden border border-panel-border bg-panel p-0 text-panel-fg ring-0 sm:max-w-3xl",
|
|
272
|
+
children: [
|
|
273
|
+
/* @__PURE__ */ jsxs(DialogHeader, { className: "border-b border-panel-border px-5 py-4", children: [
|
|
274
|
+
/* @__PURE__ */ jsx2(DialogTitle, { children: labels.imagePickerTitle }),
|
|
275
|
+
quota && /* @__PURE__ */ jsx2(DialogDescription, { className: "text-panel-muted-fg", children: labels.imagePickerQuota(quota.used, quota.limit) })
|
|
276
|
+
] }),
|
|
277
|
+
/* @__PURE__ */ jsxs("div", { className: "flex max-h-[70vh] flex-col gap-4 overflow-y-auto p-5", children: [
|
|
278
|
+
library?.upload && (full ? /* @__PURE__ */ jsx2("p", { className: "rounded-lg border border-panel-border bg-panel-elevated px-4 py-3 text-sm text-panel-muted-fg", children: labels.imagePickerQuotaFull }) : /* @__PURE__ */ jsxs(
|
|
279
|
+
"div",
|
|
357
280
|
{
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
281
|
+
onDragOver: (e) => {
|
|
282
|
+
e.preventDefault();
|
|
283
|
+
setDragging(true);
|
|
284
|
+
},
|
|
285
|
+
onDragLeave: () => setDragging(false),
|
|
286
|
+
onDrop,
|
|
287
|
+
className: cn(
|
|
288
|
+
"flex flex-col items-center justify-center gap-3 rounded-xl border-2 border-dashed px-6 py-8 text-center transition-colors",
|
|
289
|
+
dragging ? "border-brand bg-brand-soft" : "border-panel-border bg-panel-elevated"
|
|
290
|
+
),
|
|
291
|
+
children: [
|
|
292
|
+
/* @__PURE__ */ jsx2(ImageUp, { className: "size-8 text-panel-muted-fg" }),
|
|
293
|
+
/* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center justify-center gap-2 text-sm text-panel-muted-fg", children: [
|
|
294
|
+
/* @__PURE__ */ jsx2("span", { children: labels.imagePickerDrop }),
|
|
295
|
+
/* @__PURE__ */ jsx2(
|
|
296
|
+
Button,
|
|
297
|
+
{
|
|
298
|
+
type: "button",
|
|
299
|
+
size: "sm",
|
|
300
|
+
className: "bg-brand text-brand-fg hover:bg-brand/90",
|
|
301
|
+
onClick: () => fileInput.current?.click(),
|
|
302
|
+
children: labels.imagePickerBrowse
|
|
303
|
+
}
|
|
304
|
+
)
|
|
305
|
+
] }),
|
|
306
|
+
/* @__PURE__ */ jsx2(
|
|
307
|
+
"input",
|
|
308
|
+
{
|
|
309
|
+
ref: fileInput,
|
|
310
|
+
type: "file",
|
|
311
|
+
accept: library.accept ?? "image/*",
|
|
312
|
+
multiple: true,
|
|
313
|
+
hidden: true,
|
|
314
|
+
onChange: (e) => {
|
|
315
|
+
void upload(e.target.files);
|
|
316
|
+
e.target.value = "";
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
)
|
|
320
|
+
]
|
|
321
|
+
}
|
|
362
322
|
)),
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
children: /* @__PURE__ */ jsx2(
|
|
378
|
-
"img",
|
|
323
|
+
library?.allowUrl !== false && /* @__PURE__ */ jsxs(
|
|
324
|
+
"form",
|
|
325
|
+
{
|
|
326
|
+
className: "flex gap-2",
|
|
327
|
+
onSubmit: (e) => {
|
|
328
|
+
e.preventDefault();
|
|
329
|
+
const value = url.trim();
|
|
330
|
+
if (value) onSelect(value);
|
|
331
|
+
},
|
|
332
|
+
children: [
|
|
333
|
+
/* @__PURE__ */ jsxs("div", { className: "relative flex-1", children: [
|
|
334
|
+
/* @__PURE__ */ jsx2(Link2, { className: "pointer-events-none absolute top-1/2 left-2.5 size-4 -translate-y-1/2 text-panel-muted-fg" }),
|
|
335
|
+
/* @__PURE__ */ jsx2(
|
|
336
|
+
Input,
|
|
379
337
|
{
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
className: "
|
|
338
|
+
value: url,
|
|
339
|
+
onChange: (e) => setUrl(e.target.value),
|
|
340
|
+
placeholder: labels.imagePickerUrlPlaceholder,
|
|
341
|
+
className: "h-9 border-panel-border bg-panel-elevated pl-8 text-panel-fg placeholder:text-panel-muted-fg"
|
|
384
342
|
}
|
|
385
343
|
)
|
|
386
|
-
}
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
344
|
+
] }),
|
|
345
|
+
/* @__PURE__ */ jsx2(
|
|
346
|
+
Button,
|
|
347
|
+
{
|
|
348
|
+
type: "submit",
|
|
349
|
+
size: "lg",
|
|
350
|
+
variant: "outline",
|
|
351
|
+
disabled: !url.trim(),
|
|
352
|
+
className: "border-panel-border bg-panel-elevated text-panel-fg hover:bg-panel-hover hover:text-panel-fg",
|
|
353
|
+
children: labels.imagePickerUseUrl
|
|
354
|
+
}
|
|
355
|
+
)
|
|
356
|
+
]
|
|
357
|
+
}
|
|
358
|
+
),
|
|
359
|
+
library && /* @__PURE__ */ jsxs("section", { children: [
|
|
360
|
+
/* @__PURE__ */ jsx2("h3", { className: "mb-2 text-xs font-medium tracking-wide text-panel-muted-fg uppercase", children: labels.imagePickerGallery }),
|
|
361
|
+
loading && images.length === 0 && pending === 0 ? /* @__PURE__ */ jsx2("div", { className: "flex justify-center py-8", children: /* @__PURE__ */ jsx2(Spinner, {}) }) : images.length === 0 && pending === 0 ? /* @__PURE__ */ jsx2("p", { className: "py-6 text-center text-sm text-panel-muted-fg", children: labels.imagePickerEmpty }) : /* @__PURE__ */ jsxs("ul", { className: "grid grid-cols-3 gap-3 sm:grid-cols-4", children: [
|
|
362
|
+
Array.from({ length: pending }, (_, i) => /* @__PURE__ */ jsx2(
|
|
363
|
+
"li",
|
|
392
364
|
{
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
365
|
+
className: "flex aspect-[4/3] items-center justify-center rounded-lg border border-panel-border bg-panel-elevated",
|
|
366
|
+
children: /* @__PURE__ */ jsx2(Spinner, {})
|
|
367
|
+
},
|
|
368
|
+
`pending-${i}`
|
|
369
|
+
)),
|
|
370
|
+
images.map((image) => {
|
|
371
|
+
const selected = image.url === currentUrl;
|
|
372
|
+
const confirming = confirmId === image.id;
|
|
373
|
+
return /* @__PURE__ */ jsxs("li", { className: "group relative", children: [
|
|
374
|
+
/* @__PURE__ */ jsx2(
|
|
375
|
+
"button",
|
|
376
|
+
{
|
|
377
|
+
type: "button",
|
|
378
|
+
onClick: () => onSelect(image.url),
|
|
379
|
+
title: image.name,
|
|
380
|
+
className: cn(
|
|
381
|
+
"block aspect-[4/3] w-full overflow-hidden rounded-lg border bg-black/25 transition outline-none focus-visible:ring-2 focus-visible:ring-brand",
|
|
382
|
+
selected ? "border-brand ring-2 ring-brand" : "border-panel-border hover:border-panel-muted-fg"
|
|
383
|
+
),
|
|
384
|
+
children: /* @__PURE__ */ jsx2(
|
|
385
|
+
"img",
|
|
386
|
+
{
|
|
387
|
+
src: image.url,
|
|
388
|
+
alt: image.name ?? "",
|
|
389
|
+
loading: "lazy",
|
|
390
|
+
className: "size-full object-contain"
|
|
391
|
+
}
|
|
392
|
+
)
|
|
393
|
+
}
|
|
401
394
|
),
|
|
402
|
-
children:
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
395
|
+
selected && /* @__PURE__ */ jsx2("span", { className: "pointer-events-none absolute top-1.5 left-1.5 flex size-5 items-center justify-center rounded-full bg-brand text-brand-fg", children: /* @__PURE__ */ jsx2(Check, { className: "size-3" }) }),
|
|
396
|
+
image.name && /* @__PURE__ */ jsx2("span", { className: "pointer-events-none absolute inset-x-0 bottom-0 truncate rounded-b-lg bg-black/60 px-2 py-1 text-[11px] text-white opacity-0 transition group-hover:opacity-100", children: image.name }),
|
|
397
|
+
library.remove && /* @__PURE__ */ jsxs(
|
|
398
|
+
"button",
|
|
399
|
+
{
|
|
400
|
+
type: "button",
|
|
401
|
+
onClick: () => void remove(image),
|
|
402
|
+
onBlur: () => confirming && setConfirmId(null),
|
|
403
|
+
title: confirming ? labels.imagePickerConfirmDelete : labels.imagePickerDelete,
|
|
404
|
+
"aria-label": labels.imagePickerDelete,
|
|
405
|
+
className: cn(
|
|
406
|
+
"absolute top-1.5 right-1.5 flex h-6 items-center gap-1 rounded-md px-1.5 text-xs text-white transition focus-visible:opacity-100",
|
|
407
|
+
confirming ? "bg-red-600 opacity-100" : "bg-black/60 opacity-0 group-hover:opacity-100 hover:bg-black/80"
|
|
408
|
+
),
|
|
409
|
+
children: [
|
|
410
|
+
/* @__PURE__ */ jsx2(Trash2, { className: "size-3.5" }),
|
|
411
|
+
confirming && labels.imagePickerConfirmDelete
|
|
412
|
+
]
|
|
413
|
+
}
|
|
414
|
+
)
|
|
415
|
+
] }, image.id);
|
|
416
|
+
})
|
|
417
|
+
] })
|
|
418
|
+
] })
|
|
410
419
|
] })
|
|
411
|
-
]
|
|
412
|
-
|
|
413
|
-
|
|
420
|
+
]
|
|
421
|
+
}
|
|
422
|
+
) });
|
|
414
423
|
}
|
|
415
424
|
function matchesAccept(file, accept) {
|
|
416
425
|
if (!accept) return file.type.startsWith("image/");
|
|
@@ -3776,6 +3785,10 @@ function EmailEditor({
|
|
|
3776
3785
|
// app.grapesjs.com and leave a session-storage entry, which a host would then have
|
|
3777
3786
|
// to disclose and, for the entry, ask consent for.
|
|
3778
3787
|
telemetry: false,
|
|
3788
|
+
// Font Awesome from cdnjs by default, a third party a host would have to name in its
|
|
3789
|
+
// privacy policy. Nothing we show uses it: GrapesJS draws its toolbar and layer icons
|
|
3790
|
+
// as SVG, and its icon-font panels and style manager are not rendered here.
|
|
3791
|
+
cssIcons: "",
|
|
3779
3792
|
canvasCss: CANVAS_CSS,
|
|
3780
3793
|
fromElement: false,
|
|
3781
3794
|
panels: { defaults: [] },
|