@mjml-agent-editor/editor 0.4.3 → 0.5.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/dist/canvas.cjs +114 -59
- package/dist/canvas.cjs.map +1 -1
- package/dist/canvas.js +114 -59
- package/dist/canvas.js.map +1 -1
- package/package.json +1 -1
package/dist/canvas.js
CHANGED
|
@@ -1957,6 +1957,13 @@ var SCHEMA = {
|
|
|
1957
1957
|
{ attr: "direction", label: "reverseColumns", group: "advanced", control: { type: "toggle", on: "rtl" } },
|
|
1958
1958
|
{ attr: "border", label: "border", group: "advanced", control: { type: "text", placeholder: "1px solid #cccccc" } }
|
|
1959
1959
|
],
|
|
1960
|
+
// MJML allows a group these and `width` only: padding or a border fails strict. Its width is
|
|
1961
|
+
// not offered, as the canvas does not draw it: the plugin's group drops MJML's rule for it.
|
|
1962
|
+
"mj-group": [
|
|
1963
|
+
{ attr: "vertical-align", label: "verticalAlign", group: "layout", control: { type: "segmented", options: VERTICAL_ALIGN } },
|
|
1964
|
+
{ attr: "background-color", label: "backgroundColor", group: "style", control: { type: "color" } },
|
|
1965
|
+
{ attr: "direction", label: "reverseColumns", group: "advanced", control: { type: "toggle", on: "rtl" } }
|
|
1966
|
+
],
|
|
1960
1967
|
"mj-column": [
|
|
1961
1968
|
{ attr: "vertical-align", label: "verticalAlign", group: "layout", control: { type: "segmented", options: VERTICAL_ALIGN }, default: "top" },
|
|
1962
1969
|
{ attr: "padding", label: "padding", group: "spacing", control: { type: "box", units: PX_PCT } },
|
|
@@ -2315,8 +2322,12 @@ var COLUMN_RATIOS = {
|
|
|
2315
2322
|
[1, 2, 1]
|
|
2316
2323
|
]
|
|
2317
2324
|
};
|
|
2318
|
-
function columnsOf(
|
|
2319
|
-
|
|
2325
|
+
function columnsOf(container) {
|
|
2326
|
+
let children = container.components().models;
|
|
2327
|
+
const only = children.length === 1 ? children[0] : null;
|
|
2328
|
+
if (tagOf2(container) === "mj-section" && only && tagOf2(only) === "mj-group") {
|
|
2329
|
+
children = only.components().models;
|
|
2330
|
+
}
|
|
2320
2331
|
return children.length > 0 && children.every((c) => tagOf2(c) === "mj-column") ? children : null;
|
|
2321
2332
|
}
|
|
2322
2333
|
function ratioWidths(ratio) {
|
|
@@ -2379,6 +2390,7 @@ function StructureSection({
|
|
|
2379
2390
|
}) {
|
|
2380
2391
|
switch (tagOf3(component)) {
|
|
2381
2392
|
case "mj-section":
|
|
2393
|
+
case "mj-group":
|
|
2382
2394
|
return /* @__PURE__ */ jsx6(ColumnWidths, { component, labels, locked });
|
|
2383
2395
|
case "mj-social":
|
|
2384
2396
|
return /* @__PURE__ */ jsx6(
|
|
@@ -2833,11 +2845,11 @@ function ElementSettings({
|
|
|
2833
2845
|
] }),
|
|
2834
2846
|
/* @__PURE__ */ jsx7(StructureSection, { editor, component, labels, locked }),
|
|
2835
2847
|
GROUPS.map((group) => {
|
|
2836
|
-
const
|
|
2837
|
-
if (
|
|
2848
|
+
const inGroup2 = visible.filter((f) => f.group === group);
|
|
2849
|
+
if (inGroup2.length === 0) return null;
|
|
2838
2850
|
return /* @__PURE__ */ jsxs6("section", { className: "border-b border-panel-border/60 py-2", children: [
|
|
2839
2851
|
/* @__PURE__ */ jsx7("h3", { className: "px-3 pb-1 text-[10px] font-semibold tracking-wider text-panel-muted-fg/80 uppercase", children: groupName(group, labels) }),
|
|
2840
|
-
|
|
2852
|
+
inGroup2.map(row)
|
|
2841
2853
|
] }, group);
|
|
2842
2854
|
}),
|
|
2843
2855
|
advancedCount > 0 && /* @__PURE__ */ jsxs6("section", { className: "py-1", children: [
|
|
@@ -3146,7 +3158,9 @@ function registerBlocks(editor, blocks, category, media) {
|
|
|
3146
3158
|
category,
|
|
3147
3159
|
media: block.media ?? media,
|
|
3148
3160
|
content: withClosedTags(block.mjml),
|
|
3149
|
-
attributes: tileAttributes(block.id)
|
|
3161
|
+
attributes: tileAttributes(block.id),
|
|
3162
|
+
// Selected once dropped, as grapesjs-mjml's own blocks are, so its Settings open.
|
|
3163
|
+
select: true
|
|
3150
3164
|
});
|
|
3151
3165
|
}
|
|
3152
3166
|
}
|
|
@@ -3189,6 +3203,15 @@ function builtinBlock(labels) {
|
|
|
3189
3203
|
}
|
|
3190
3204
|
var CANVAS_CSS = 'img:not([src]), img[src=""] { min-height: 120px; background: #f4f4f5; outline: 1px dashed #a1a1aa; outline-offset: -1px; }';
|
|
3191
3205
|
|
|
3206
|
+
// src/components/editor/settings-follow.ts
|
|
3207
|
+
function clickShowsSettings(selected, target) {
|
|
3208
|
+
return !!selected && !!target && selected.contains(target);
|
|
3209
|
+
}
|
|
3210
|
+
function dropShowsSettings(added, selected) {
|
|
3211
|
+
const first = Array.isArray(added) ? added[0] : added;
|
|
3212
|
+
return first !== void 0 && first === selected;
|
|
3213
|
+
}
|
|
3214
|
+
|
|
3192
3215
|
// src/components/editor/table-type.ts
|
|
3193
3216
|
import { isSafeLink as isSafeLink2 } from "@mjml-agent-editor/core";
|
|
3194
3217
|
var DROPPED = "script, style, link, iframe, frame, object, embed, applet, base, meta, animate, set, svg, math, template";
|
|
@@ -3284,12 +3307,17 @@ function fixPluginTypes(editor) {
|
|
|
3284
3307
|
for (const type of editor.Components.getTypes()) {
|
|
3285
3308
|
if (!type.id.startsWith("mj-") || type.id === "mj-table") continue;
|
|
3286
3309
|
const base = type.model.prototype.getAttrToHTML;
|
|
3310
|
+
const view = type.view.prototype;
|
|
3287
3311
|
editor.Components.addType(type.id, {
|
|
3312
|
+
...type.id === "mj-column" && { view: groupedColumnView(view) },
|
|
3313
|
+
...type.id === "mj-group" && { view: redrawnGroupView(view) },
|
|
3288
3314
|
model: {
|
|
3289
3315
|
defaults: {
|
|
3290
3316
|
"style-default": {},
|
|
3291
3317
|
...type.id === "mj-column" && {
|
|
3292
|
-
droppable: (source) => COLUMN_CHILDREN.has(String(source.get("tagName")))
|
|
3318
|
+
droppable: (source) => COLUMN_CHILDREN.has(String(source.get("tagName"))),
|
|
3319
|
+
// The plugin moves a column only within sections, so a group's could not even swap.
|
|
3320
|
+
draggable: '[data-gjs-type="mj-section"], [data-gjs-type="mj-group"]'
|
|
3293
3321
|
},
|
|
3294
3322
|
// GrapesJS's own is an SVG's markup, which the canvas wrote into `src` and saved.
|
|
3295
3323
|
...type.id === "mj-image" && { src: "" }
|
|
@@ -3306,6 +3334,41 @@ function fixPluginTypes(editor) {
|
|
|
3306
3334
|
});
|
|
3307
3335
|
}
|
|
3308
3336
|
}
|
|
3337
|
+
var inGroup = (column) => column.parent()?.get("type") === "mj-group";
|
|
3338
|
+
function groupedColumnView(base) {
|
|
3339
|
+
return {
|
|
3340
|
+
getMjmlTemplate() {
|
|
3341
|
+
if (!inGroup(this.model)) return base.getMjmlTemplate.call(this);
|
|
3342
|
+
const siblings = "<mj-column></mj-column>".repeat(
|
|
3343
|
+
Math.max((this.model.collection?.length ?? 1) - 1, 0)
|
|
3344
|
+
);
|
|
3345
|
+
const width = (c) => {
|
|
3346
|
+
const value = c?.getAttributes().width;
|
|
3347
|
+
return typeof value === "string" && value ? ` width="${value.replace(/"/g, "")}"` : "";
|
|
3348
|
+
};
|
|
3349
|
+
let body = this.model;
|
|
3350
|
+
while (body && body.get("type") !== "mj-body") body = body.parent();
|
|
3351
|
+
return {
|
|
3352
|
+
start: `<mjml><mj-body${width(body)}><mj-section><mj-group${width(this.model.parent())}>`,
|
|
3353
|
+
end: `${siblings}</mj-group></mj-section></mj-body></mjml>`
|
|
3354
|
+
};
|
|
3355
|
+
},
|
|
3356
|
+
getTemplateFromEl(container) {
|
|
3357
|
+
if (!inGroup(this.model)) return base.getTemplateFromEl.call(this, container);
|
|
3358
|
+
return container.firstChild?.querySelector(
|
|
3359
|
+
"div > table > tbody > tr > td > div > div"
|
|
3360
|
+
) ?? null;
|
|
3361
|
+
}
|
|
3362
|
+
};
|
|
3363
|
+
}
|
|
3364
|
+
function redrawnGroupView(base) {
|
|
3365
|
+
return {
|
|
3366
|
+
init() {
|
|
3367
|
+
base.init.call(this);
|
|
3368
|
+
this.listenTo(this.model.components(), "add remove", this.render);
|
|
3369
|
+
}
|
|
3370
|
+
};
|
|
3371
|
+
}
|
|
3309
3372
|
|
|
3310
3373
|
// src/components/editor/EmailEditor.tsx
|
|
3311
3374
|
import { jsx as jsx8, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
@@ -3431,7 +3494,6 @@ function EmailEditor({
|
|
|
3431
3494
|
requestAnimationFrame(() => blockSearchRef.current?.focus());
|
|
3432
3495
|
};
|
|
3433
3496
|
const [composeTarget, setComposeTarget] = useState5(null);
|
|
3434
|
-
const selectionSource = useRef5("other");
|
|
3435
3497
|
const editorRef = useRef5(null);
|
|
3436
3498
|
const { running: turnRunning, locked: readOnly, setLocked } = useAgentTurn();
|
|
3437
3499
|
const readOnlyRef = useRef5(false);
|
|
@@ -3446,9 +3508,10 @@ function EmailEditor({
|
|
|
3446
3508
|
setLocked(false);
|
|
3447
3509
|
};
|
|
3448
3510
|
}, [turn, setLocked]);
|
|
3511
|
+
const inert = readOnly || loading;
|
|
3449
3512
|
const wasLocked = useRef5(false);
|
|
3450
3513
|
useEffect5(() => {
|
|
3451
|
-
if (
|
|
3514
|
+
if (inert) {
|
|
3452
3515
|
wasLocked.current = true;
|
|
3453
3516
|
return;
|
|
3454
3517
|
}
|
|
@@ -3463,7 +3526,7 @@ function EmailEditor({
|
|
|
3463
3526
|
cancelAnimationFrame(frame);
|
|
3464
3527
|
root.style.overflow = overflow;
|
|
3465
3528
|
};
|
|
3466
|
-
}, [
|
|
3529
|
+
}, [inert]);
|
|
3467
3530
|
const [crumbs, setCrumbs] = useState5([]);
|
|
3468
3531
|
const [picker, setPicker] = useState5(
|
|
3469
3532
|
null
|
|
@@ -3565,7 +3628,7 @@ function EmailEditor({
|
|
|
3565
3628
|
fixPluginTypes(editor);
|
|
3566
3629
|
registerBlocks(editor, blocks, labels.blockCategorySections, SECTION_SVG);
|
|
3567
3630
|
setupLayerNames(editor, labels);
|
|
3568
|
-
const STRUCTURE = /* @__PURE__ */ new Set(["mj-wrapper", "mj-section", "mj-column"]);
|
|
3631
|
+
const STRUCTURE = /* @__PURE__ */ new Set(["mj-wrapper", "mj-section", "mj-group", "mj-column"]);
|
|
3569
3632
|
const showBodyInLayers = () => {
|
|
3570
3633
|
const body = findByTag("mj-body");
|
|
3571
3634
|
if (!body) return;
|
|
@@ -3586,23 +3649,24 @@ function EmailEditor({
|
|
|
3586
3649
|
decorate(c);
|
|
3587
3650
|
});
|
|
3588
3651
|
editor.on("load", () => {
|
|
3589
|
-
|
|
3590
|
-
|
|
3591
|
-
|
|
3592
|
-
|
|
3593
|
-
|
|
3594
|
-
|
|
3595
|
-
|
|
3652
|
+
editor.Canvas.getBody()?.addEventListener(
|
|
3653
|
+
"click",
|
|
3654
|
+
(e) => setTimeout(() => {
|
|
3655
|
+
if (clickShowsSettings(editor.getSelected()?.getEl(), e.target)) {
|
|
3656
|
+
setSidebarView("settings");
|
|
3657
|
+
}
|
|
3658
|
+
})
|
|
3596
3659
|
);
|
|
3597
3660
|
editor.Canvas.getDocument()?.addEventListener("keydown", onBlockSearchKey);
|
|
3598
3661
|
showBodyInLayers();
|
|
3599
3662
|
});
|
|
3600
3663
|
editor.on("component:selected", (c) => {
|
|
3601
3664
|
keepStructureOpen();
|
|
3602
|
-
if (selectionSource.current === "canvas") setSidebarView("settings");
|
|
3603
|
-
selectionSource.current = "other";
|
|
3604
3665
|
buildCrumbs(c);
|
|
3605
3666
|
});
|
|
3667
|
+
editor.on("block:drag:stop", (added) => {
|
|
3668
|
+
if (dropShowsSettings(added, editor.getSelected())) setSidebarView("settings");
|
|
3669
|
+
});
|
|
3606
3670
|
editor.on("component:deselected", () => {
|
|
3607
3671
|
buildCrumbs(editor.getSelected());
|
|
3608
3672
|
});
|
|
@@ -3851,9 +3915,14 @@ function EmailEditor({
|
|
|
3851
3915
|
"div",
|
|
3852
3916
|
{
|
|
3853
3917
|
className: "relative flex h-full min-h-0 flex-1",
|
|
3854
|
-
inert
|
|
3918
|
+
inert,
|
|
3855
3919
|
onKeyDown: (e) => onBlockSearchKey(e.nativeEvent),
|
|
3856
3920
|
children: [
|
|
3921
|
+
loading && /* @__PURE__ */ jsxs7("div", { className: "absolute inset-0 z-30 flex items-center justify-center gap-2 bg-white/70 text-sm text-zinc-500", children: [
|
|
3922
|
+
/* @__PURE__ */ jsx8(Spinner, {}),
|
|
3923
|
+
" ",
|
|
3924
|
+
labels.loadingEditor
|
|
3925
|
+
] }),
|
|
3857
3926
|
readOnly && /* @__PURE__ */ jsx8("div", { className: "absolute inset-0 z-30 flex cursor-not-allowed items-start justify-center bg-white/20 pt-3", children: /* @__PURE__ */ jsxs7("span", { className: "flex items-center gap-2 rounded-full bg-panel px-3 py-1 text-xs text-panel-fg shadow", children: [
|
|
3858
3927
|
/* @__PURE__ */ jsx8(Spinner, {}),
|
|
3859
3928
|
" ",
|
|
@@ -3864,7 +3933,6 @@ function EmailEditor({
|
|
|
3864
3933
|
{
|
|
3865
3934
|
view: sidebarView,
|
|
3866
3935
|
onViewChange: setSidebarView,
|
|
3867
|
-
markLayersSource: () => selectionSource.current = "layers",
|
|
3868
3936
|
blockSearchRef
|
|
3869
3937
|
}
|
|
3870
3938
|
),
|
|
@@ -3895,12 +3963,7 @@ function EmailEditor({
|
|
|
3895
3963
|
editorRef.current?.AssetManager.close();
|
|
3896
3964
|
}
|
|
3897
3965
|
}
|
|
3898
|
-
)
|
|
3899
|
-
loading && /* @__PURE__ */ jsxs7("div", { className: "absolute inset-0 z-10 flex items-center justify-center gap-2 bg-white/70 text-sm text-zinc-500", children: [
|
|
3900
|
-
/* @__PURE__ */ jsx8(Spinner, {}),
|
|
3901
|
-
" ",
|
|
3902
|
-
labels.loadingEditor
|
|
3903
|
-
] })
|
|
3966
|
+
)
|
|
3904
3967
|
] }),
|
|
3905
3968
|
/* @__PURE__ */ jsx8(Breadcrumb, { crumbs, onSelect: (c) => editorRef.current?.select(c) })
|
|
3906
3969
|
] })
|
|
@@ -3913,7 +3976,6 @@ function EmailEditor({
|
|
|
3913
3976
|
function LeftSidebar({
|
|
3914
3977
|
view,
|
|
3915
3978
|
onViewChange,
|
|
3916
|
-
markLayersSource,
|
|
3917
3979
|
blockSearchRef
|
|
3918
3980
|
}) {
|
|
3919
3981
|
const editor = useEditorMaybe2();
|
|
@@ -4021,36 +4083,29 @@ function LeftSidebar({
|
|
|
4021
4083
|
] })
|
|
4022
4084
|
}
|
|
4023
4085
|
) }),
|
|
4024
|
-
/* @__PURE__ */ jsxs7(
|
|
4025
|
-
"
|
|
4026
|
-
|
|
4027
|
-
|
|
4028
|
-
|
|
4029
|
-
|
|
4030
|
-
|
|
4031
|
-
|
|
4032
|
-
|
|
4033
|
-
|
|
4034
|
-
|
|
4035
|
-
|
|
4036
|
-
|
|
4037
|
-
|
|
4038
|
-
|
|
4039
|
-
|
|
4040
|
-
|
|
4041
|
-
|
|
4042
|
-
"
|
|
4043
|
-
|
|
4044
|
-
|
|
4045
|
-
|
|
4046
|
-
|
|
4047
|
-
if (e.target.closest("[data-name]")) e.stopPropagation();
|
|
4048
|
-
}
|
|
4049
|
-
}
|
|
4050
|
-
)
|
|
4051
|
-
]
|
|
4052
|
-
}
|
|
4053
|
-
)
|
|
4086
|
+
/* @__PURE__ */ jsxs7("div", { className: cn("flex min-h-0 flex-1 flex-col", view !== "layers" && "hidden"), children: [
|
|
4087
|
+
/* @__PURE__ */ jsxs7("label", { className: "flex items-center justify-between gap-2 border-b border-panel-border px-3 py-2 text-xs text-panel-muted-fg", children: [
|
|
4088
|
+
/* @__PURE__ */ jsx8("span", { children: labels.expandAll }),
|
|
4089
|
+
/* @__PURE__ */ jsx8(
|
|
4090
|
+
Switch,
|
|
4091
|
+
{
|
|
4092
|
+
checked: layersExpanded,
|
|
4093
|
+
onCheckedChange: toggleLayers,
|
|
4094
|
+
className: "data-[state=checked]:bg-brand data-[state=unchecked]:bg-panel-border"
|
|
4095
|
+
}
|
|
4096
|
+
)
|
|
4097
|
+
] }),
|
|
4098
|
+
/* @__PURE__ */ jsx8(
|
|
4099
|
+
"div",
|
|
4100
|
+
{
|
|
4101
|
+
id: "gjs-layers",
|
|
4102
|
+
className: "min-h-0 flex-1 overflow-y-auto",
|
|
4103
|
+
onDoubleClickCapture: (e) => {
|
|
4104
|
+
if (e.target.closest("[data-name]")) e.stopPropagation();
|
|
4105
|
+
}
|
|
4106
|
+
}
|
|
4107
|
+
)
|
|
4108
|
+
] })
|
|
4054
4109
|
] });
|
|
4055
4110
|
}
|
|
4056
4111
|
function Breadcrumb({
|