best-email-builder 0.13.0 → 0.14.0
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/CHANGELOG.md +25 -0
- package/dist/ai-server.js +3 -3
- package/dist/ai.js +6 -6
- package/dist/builder.js +10 -8
- package/dist/{chunk-G7G3TRWG.js → chunk-2ZRGQFX6.js} +684 -700
- package/dist/chunk-2ZRGQFX6.js.map +1 -0
- package/dist/{chunk-UGF3YJ64.js → chunk-7JJ4EZAZ.js} +2 -2
- package/dist/{chunk-LSOR4OII.js → chunk-NEEOZR5C.js} +68 -68
- package/dist/chunk-NEEOZR5C.js.map +1 -0
- package/dist/{chunk-6QTSXJG4.js → chunk-O2A2DPFJ.js} +3 -3
- package/dist/{chunk-JJ56652Y.js → chunk-OTE7BRKU.js} +9 -5
- package/dist/chunk-OTE7BRKU.js.map +1 -0
- package/dist/{chunk-2KOI5SWV.js → chunk-PCSKVVSZ.js} +2 -2
- package/dist/{chunk-A4KF7NMP.js → chunk-R3DWMSSR.js} +2 -2
- package/dist/{chunk-Q5UPFM7J.js → chunk-SAYIBNH5.js} +3 -3
- package/dist/{chunk-Q5UPFM7J.js.map → chunk-SAYIBNH5.js.map} +1 -1
- package/dist/{chunk-ZTUW25BG.js → chunk-SYEUC7I2.js} +24 -21
- package/dist/chunk-SYEUC7I2.js.map +1 -0
- package/dist/{chunk-Q2ZUJFOY.js → chunk-VHTV32EW.js} +2 -2
- package/dist/{chunk-XF25DLD4.js → chunk-XXUYQ5BX.js} +3 -3
- package/dist/index.js +12 -10
- package/dist/index.js.map +1 -1
- package/dist/library.js +5 -5
- package/dist/server.js +7 -5
- package/dist/server.js.map +1 -1
- package/dist/styles.css +1 -1
- package/dist/types/builder.d.ts +1 -1
- package/dist/types/core/constants.d.ts +1 -1
- package/dist/types/engine/NativeAdapter.d.ts +1 -1
- package/dist/types/features/variables/VariablePicker.d.ts +2 -2
- package/dist/types/features/variables/VariablesBar.d.ts +10 -5
- package/dist/types/features/variables/catalog.d.ts +15 -1
- package/dist/types/index.d.ts +1 -1
- package/dist/types/server/index.d.ts +1 -1
- package/dist/types/ui/primitives/searchShortcut.d.ts +1 -1
- package/package.json +1 -1
- package/dist/chunk-G7G3TRWG.js.map +0 -1
- package/dist/chunk-JJ56652Y.js.map +0 -1
- package/dist/chunk-LSOR4OII.js.map +0 -1
- package/dist/chunk-ZTUW25BG.js.map +0 -1
- package/dist/types/features/variables/VariablesMenu.d.ts +0 -18
- /package/dist/{chunk-UGF3YJ64.js.map → chunk-7JJ4EZAZ.js.map} +0 -0
- /package/dist/{chunk-6QTSXJG4.js.map → chunk-O2A2DPFJ.js.map} +0 -0
- /package/dist/{chunk-2KOI5SWV.js.map → chunk-PCSKVVSZ.js.map} +0 -0
- /package/dist/{chunk-A4KF7NMP.js.map → chunk-R3DWMSSR.js.map} +0 -0
- /package/dist/{chunk-Q2ZUJFOY.js.map → chunk-VHTV32EW.js.map} +0 -0
- /package/dist/{chunk-XF25DLD4.js.map → chunk-XXUYQ5BX.js.map} +0 -0
|
@@ -6,17 +6,17 @@ import {
|
|
|
6
6
|
useLayoutWidth,
|
|
7
7
|
useSearchShortcut,
|
|
8
8
|
withThumbnailImages
|
|
9
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-NEEOZR5C.js";
|
|
10
10
|
import {
|
|
11
11
|
BRAND_NOTES,
|
|
12
12
|
countImagesWithoutPicture,
|
|
13
13
|
logoWidthFor
|
|
14
|
-
} from "./chunk-
|
|
14
|
+
} from "./chunk-VHTV32EW.js";
|
|
15
15
|
import {
|
|
16
16
|
cleanRichText,
|
|
17
17
|
escapeText,
|
|
18
18
|
textOfHtml
|
|
19
|
-
} from "./chunk-
|
|
19
|
+
} from "./chunk-7JJ4EZAZ.js";
|
|
20
20
|
import {
|
|
21
21
|
Button,
|
|
22
22
|
Dialog,
|
|
@@ -51,7 +51,7 @@ import {
|
|
|
51
51
|
createHttpStorage,
|
|
52
52
|
useT,
|
|
53
53
|
useUi
|
|
54
|
-
} from "./chunk-
|
|
54
|
+
} from "./chunk-O2A2DPFJ.js";
|
|
55
55
|
import {
|
|
56
56
|
DEFAULT_BUTTON_URL,
|
|
57
57
|
EMAIL_BUILDER_VERSION,
|
|
@@ -94,10 +94,10 @@ import {
|
|
|
94
94
|
setAttributes,
|
|
95
95
|
setContent,
|
|
96
96
|
toProject
|
|
97
|
-
} from "./chunk-
|
|
97
|
+
} from "./chunk-SAYIBNH5.js";
|
|
98
98
|
import {
|
|
99
99
|
isSafeUrl
|
|
100
|
-
} from "./chunk-
|
|
100
|
+
} from "./chunk-R3DWMSSR.js";
|
|
101
101
|
import {
|
|
102
102
|
CUSTOM_GROUP,
|
|
103
103
|
DEFAULT_VARIABLES,
|
|
@@ -110,7 +110,7 @@ import {
|
|
|
110
110
|
tokenOf,
|
|
111
111
|
translatorFor,
|
|
112
112
|
validateCustomVariable
|
|
113
|
-
} from "./chunk-
|
|
113
|
+
} from "./chunk-SYEUC7I2.js";
|
|
114
114
|
|
|
115
115
|
// src/core/events.js
|
|
116
116
|
function createEmitter({ onError } = {}) {
|
|
@@ -1113,7 +1113,7 @@ function createStarterMjml({ sender = {}, buttonUrl = DEFAULT_BUTTON_URL, t = id
|
|
|
1113
1113
|
`<mj-section background-color="${PALETTE.surface}" padding-top="8px" padding-bottom="28px">`,
|
|
1114
1114
|
"<mj-column>",
|
|
1115
1115
|
`<mj-text ${TEXT} font-size="24px" line-height="32px" font-weight="bold" ${SIDE}>${escapeXml2(t("Your headline"))}</mj-text>`,
|
|
1116
|
-
`<mj-text ${TEXT} font-size="15px" line-height="24px" ${SIDE}>${escapeXml2(t("Hello
|
|
1116
|
+
`<mj-text ${TEXT} font-size="15px" line-height="24px" ${SIDE}>${escapeXml2(t("Hello,"))}<br /><br />${escapeXml2(t("Your text."))}</mj-text>`,
|
|
1117
1117
|
`<mj-button href="${escapeXml2(buttonUrl)}" background-color="${PALETTE.navy}" color="${PALETTE.white}" font-family="${FONT_STACK}" font-size="15px" font-weight="bold" border-radius="6px" inner-padding="12px 28px" align="left" ${SIDE}>${escapeXml2(t("Learn more"))}</mj-button>`,
|
|
1118
1118
|
"</mj-column>",
|
|
1119
1119
|
"</mj-section>",
|
|
@@ -1223,7 +1223,7 @@ var TEMPLATE_GALLERY = [
|
|
|
1223
1223
|
create: ({ sender, t }) => email(
|
|
1224
1224
|
header(sender, t) + section(
|
|
1225
1225
|
column(
|
|
1226
|
-
heading(esc(t("Welcome
|
|
1226
|
+
heading(esc(t("Welcome aboard!")), { size: 28 }) + paragraph(esc(t("Thank you for joining us. Here is how to get the most out of your account.")))
|
|
1227
1227
|
),
|
|
1228
1228
|
{ top: 16, bottom: 8 }
|
|
1229
1229
|
) + section(
|
|
@@ -1438,112 +1438,20 @@ var versionsFeature = {
|
|
|
1438
1438
|
};
|
|
1439
1439
|
|
|
1440
1440
|
// src/features/variables/VariablesBar.jsx
|
|
1441
|
-
import { useId, useState as
|
|
1442
|
-
import { Braces, ChevronDown } from "lucide-react";
|
|
1443
|
-
import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
1444
|
-
function VariablesBar({ context }) {
|
|
1445
|
-
const { editor, ready, variables } = context;
|
|
1446
|
-
const { t, notify } = useUi();
|
|
1447
|
-
const [hovered, setHovered] = useState2(false);
|
|
1448
|
-
const [open, setOpen] = useState2(false);
|
|
1449
|
-
const listId = useId();
|
|
1450
|
-
const fields = variables.filter((v) => v.kind !== "link");
|
|
1451
|
-
if (!fields.length) return null;
|
|
1452
|
-
const expanded = hovered || open;
|
|
1453
|
-
const insert = (variable) => {
|
|
1454
|
-
if (!editor.insertText(tokenOf(variable))) notify("info", t("Select a text or a button first."));
|
|
1455
|
-
};
|
|
1456
|
-
return /* @__PURE__ */ jsx2("div", { className: "eb:relative eb:z-10 eb:h-6 eb:shrink-0", "data-testid": "eb-variables-bar", children: /* @__PURE__ */ jsxs2(
|
|
1457
|
-
"div",
|
|
1458
|
-
{
|
|
1459
|
-
className: cn(
|
|
1460
|
-
"eb:absolute eb:inset-x-0 eb:top-0 eb:border-b eb:border-border eb:bg-background eb:transition-shadow",
|
|
1461
|
-
expanded && "eb:shadow-md"
|
|
1462
|
-
),
|
|
1463
|
-
"data-expanded": expanded,
|
|
1464
|
-
onMouseEnter: () => setHovered(true),
|
|
1465
|
-
onMouseLeave: () => setHovered(false),
|
|
1466
|
-
onKeyDown: (e) => e.key === "Escape" && open && setOpen(false),
|
|
1467
|
-
onMouseDown: (e) => e.preventDefault(),
|
|
1468
|
-
children: [
|
|
1469
|
-
/* @__PURE__ */ jsxs2(
|
|
1470
|
-
"button",
|
|
1471
|
-
{
|
|
1472
|
-
type: "button",
|
|
1473
|
-
"aria-expanded": expanded,
|
|
1474
|
-
"aria-controls": listId,
|
|
1475
|
-
onClick: () => setOpen((value) => !value),
|
|
1476
|
-
className: "eb:flex eb:h-6 eb:w-full eb:items-center eb:gap-1.5 eb:px-3 eb:text-[11px] eb:font-medium eb:text-foreground-lighter eb:outline-none eb:hover:text-foreground eb:focus-visible:ring-2 eb:focus-visible:ring-ring/40 eb:focus-visible:ring-inset",
|
|
1477
|
-
children: [
|
|
1478
|
-
/* @__PURE__ */ jsx2(Braces, { className: "eb:size-3", "aria-hidden": "true" }),
|
|
1479
|
-
t("Personalization fields"),
|
|
1480
|
-
/* @__PURE__ */ jsx2("span", { className: "eb:text-foreground-muted", children: fields.length }),
|
|
1481
|
-
/* @__PURE__ */ jsx2(ChevronDown, { className: cn("eb:ml-auto eb:size-3.5 eb:transition-transform", expanded && "eb:rotate-180"), "aria-hidden": "true" })
|
|
1482
|
-
]
|
|
1483
|
-
}
|
|
1484
|
-
),
|
|
1485
|
-
/* @__PURE__ */ jsx2(
|
|
1486
|
-
"div",
|
|
1487
|
-
{
|
|
1488
|
-
id: listId,
|
|
1489
|
-
className: cn(
|
|
1490
|
-
"eb:grid eb:transition-[grid-template-rows,visibility] eb:duration-200 eb:ease-out",
|
|
1491
|
-
expanded ? "eb:visible eb:grid-rows-[1fr]" : "eb:invisible eb:grid-rows-[0fr]"
|
|
1492
|
-
),
|
|
1493
|
-
children: /* @__PURE__ */ jsx2("div", { className: "eb:min-h-0 eb:overflow-hidden", children: /* @__PURE__ */ jsx2(
|
|
1494
|
-
"div",
|
|
1495
|
-
{
|
|
1496
|
-
role: "group",
|
|
1497
|
-
"aria-label": t("Personalization fields"),
|
|
1498
|
-
className: "eb:flex eb:max-h-24 eb:flex-wrap eb:gap-1 eb:overflow-y-auto eb:px-3 eb:pt-0.5 eb:pb-2",
|
|
1499
|
-
children: fields.map((variable) => /* @__PURE__ */ jsx2(
|
|
1500
|
-
TooltipWrap,
|
|
1501
|
-
{
|
|
1502
|
-
label: /* @__PURE__ */ jsx2("code", { className: "eb:font-mono", children: tokenOf(variable) }),
|
|
1503
|
-
delayDuration: 100,
|
|
1504
|
-
hoverable: false,
|
|
1505
|
-
children: /* @__PURE__ */ jsx2(
|
|
1506
|
-
"button",
|
|
1507
|
-
{
|
|
1508
|
-
type: "button",
|
|
1509
|
-
disabled: !ready,
|
|
1510
|
-
"data-variable": variable.key,
|
|
1511
|
-
onMouseEnter: () => editor.previewText(tokenOf(variable)),
|
|
1512
|
-
onMouseLeave: () => editor.clearPreview(),
|
|
1513
|
-
onFocus: () => editor.previewText(tokenOf(variable)),
|
|
1514
|
-
onBlur: () => editor.clearPreview(),
|
|
1515
|
-
onClick: () => insert(variable),
|
|
1516
|
-
className: "eb:inline-flex eb:h-5.5 eb:items-center eb:rounded-full eb:border eb:border-border eb:bg-surface-75 eb:px-2 eb:text-[11px] eb:font-medium eb:text-foreground eb:transition-colors eb:outline-none eb:hover:border-primary/50 eb:hover:bg-primary/5 eb:hover:text-primary eb:focus-visible:ring-2 eb:focus-visible:ring-ring/50 eb:disabled:opacity-50",
|
|
1517
|
-
children: variable.custom ? variable.label : t(variable.label)
|
|
1518
|
-
}
|
|
1519
|
-
)
|
|
1520
|
-
},
|
|
1521
|
-
variable.key
|
|
1522
|
-
))
|
|
1523
|
-
}
|
|
1524
|
-
) })
|
|
1525
|
-
}
|
|
1526
|
-
)
|
|
1527
|
-
]
|
|
1528
|
-
}
|
|
1529
|
-
) });
|
|
1530
|
-
}
|
|
1531
|
-
|
|
1532
|
-
// src/features/variables/VariablesMenu.jsx
|
|
1533
|
-
import { useState as useState5 } from "react";
|
|
1534
|
-
import { Braces as Braces3 } from "lucide-react";
|
|
1441
|
+
import { Fragment, useId, useState as useState3 } from "react";
|
|
1442
|
+
import { Braces as Braces2, ChevronDown, Link2, Pencil, Plus } from "lucide-react";
|
|
1535
1443
|
|
|
1536
1444
|
// src/features/variables/VariableDialog.jsx
|
|
1537
|
-
import { useEffect, useMemo, useState as
|
|
1538
|
-
import { Braces
|
|
1539
|
-
import { jsx as
|
|
1445
|
+
import { useEffect, useMemo, useState as useState2 } from "react";
|
|
1446
|
+
import { Braces, Loader2, Trash2 } from "lucide-react";
|
|
1447
|
+
import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
1540
1448
|
function VariableDialog({ open, onOpenChange, variable = null, initialLabel = "", variables, storage, onSaved, onDeleted }) {
|
|
1541
1449
|
const { t, notify, confirm } = useUi();
|
|
1542
1450
|
const editing = Boolean(variable);
|
|
1543
|
-
const [form, setForm] =
|
|
1544
|
-
const [keyTouched, setKeyTouched] =
|
|
1545
|
-
const [errors, setErrors] =
|
|
1546
|
-
const [busy, setBusy] =
|
|
1451
|
+
const [form, setForm] = useState2({ label: "", key: "", group: "", sample: "", fallback: "" });
|
|
1452
|
+
const [keyTouched, setKeyTouched] = useState2(false);
|
|
1453
|
+
const [errors, setErrors] = useState2({});
|
|
1454
|
+
const [busy, setBusy] = useState2(false);
|
|
1547
1455
|
useEffect(() => {
|
|
1548
1456
|
if (!open) return;
|
|
1549
1457
|
setForm(
|
|
@@ -1610,9 +1518,9 @@ function VariableDialog({ open, onOpenChange, variable = null, initialLabel = ""
|
|
|
1610
1518
|
setBusy(false);
|
|
1611
1519
|
}
|
|
1612
1520
|
};
|
|
1613
|
-
const field = (name, label, props = {}, hint = null) => /* @__PURE__ */
|
|
1614
|
-
/* @__PURE__ */
|
|
1615
|
-
/* @__PURE__ */
|
|
1521
|
+
const field = (name, label, props = {}, hint = null) => /* @__PURE__ */ jsxs2("div", { className: "eb:space-y-1.5", children: [
|
|
1522
|
+
/* @__PURE__ */ jsx2(Label, { htmlFor: `eb-var-${name}`, children: label }),
|
|
1523
|
+
/* @__PURE__ */ jsx2(
|
|
1616
1524
|
Input,
|
|
1617
1525
|
{
|
|
1618
1526
|
id: `eb-var-${name}`,
|
|
@@ -1623,23 +1531,23 @@ function VariableDialog({ open, onOpenChange, variable = null, initialLabel = ""
|
|
|
1623
1531
|
...props
|
|
1624
1532
|
}
|
|
1625
1533
|
),
|
|
1626
|
-
(errors[name] || hint) && /* @__PURE__ */
|
|
1534
|
+
(errors[name] || hint) && /* @__PURE__ */ jsx2("p", { id: `eb-var-${name}-hint`, className: errors[name] ? "eb:text-[12px] eb:text-destructive" : "eb:text-[11px] eb:text-foreground-muted", children: errors[name] || hint })
|
|
1627
1535
|
] });
|
|
1628
|
-
return /* @__PURE__ */
|
|
1536
|
+
return /* @__PURE__ */ jsx2(Dialog, { open, onOpenChange, children: /* @__PURE__ */ jsxs2(
|
|
1629
1537
|
DialogContent,
|
|
1630
1538
|
{
|
|
1631
1539
|
className: "eb:sm:max-w-[440px]",
|
|
1632
1540
|
onMouseDown: (e) => e.stopPropagation(),
|
|
1633
1541
|
onCloseAutoFocus: (e) => e.preventDefault(),
|
|
1634
1542
|
children: [
|
|
1635
|
-
/* @__PURE__ */
|
|
1636
|
-
/* @__PURE__ */
|
|
1637
|
-
/* @__PURE__ */
|
|
1638
|
-
/* @__PURE__ */
|
|
1639
|
-
/* @__PURE__ */
|
|
1543
|
+
/* @__PURE__ */ jsx2(DialogHeader, { children: /* @__PURE__ */ jsxs2("div", { className: "eb:flex eb:items-center eb:gap-3", children: [
|
|
1544
|
+
/* @__PURE__ */ jsx2("span", { className: "eb:flex eb:size-10 eb:shrink-0 eb:items-center eb:justify-center eb:rounded-lg2 eb:bg-primary/10 eb:text-primary", children: /* @__PURE__ */ jsx2(Braces, { className: "eb:size-5" }) }),
|
|
1545
|
+
/* @__PURE__ */ jsxs2("div", { children: [
|
|
1546
|
+
/* @__PURE__ */ jsx2(DialogTitle, { className: "eb:text-base eb:font-normal", children: editing ? t("Edit variable") : t("New variable") }),
|
|
1547
|
+
/* @__PURE__ */ jsx2(DialogDescription, { className: "eb:text-[13px]", children: t("Available in every template.") })
|
|
1640
1548
|
] })
|
|
1641
1549
|
] }) }),
|
|
1642
|
-
/* @__PURE__ */
|
|
1550
|
+
/* @__PURE__ */ jsxs2("form", { onSubmit: submit, className: "eb:space-y-3", noValidate: true, children: [
|
|
1643
1551
|
field("label", t("Name"), { autoFocus: true, maxLength: VARIABLE_LIMITS.label, placeholder: t("Order number") }),
|
|
1644
1552
|
field(
|
|
1645
1553
|
"key",
|
|
@@ -1647,9 +1555,9 @@ function VariableDialog({ open, onOpenChange, variable = null, initialLabel = ""
|
|
|
1647
1555
|
{ disabled: editing, maxLength: VARIABLE_LIMITS.key, className: "eb:font-mono eb:text-[12px]" },
|
|
1648
1556
|
form.key ? `{{${form.key}}}` : null
|
|
1649
1557
|
),
|
|
1650
|
-
/* @__PURE__ */
|
|
1651
|
-
/* @__PURE__ */
|
|
1652
|
-
/* @__PURE__ */
|
|
1558
|
+
/* @__PURE__ */ jsxs2("div", { className: "eb:space-y-1.5", children: [
|
|
1559
|
+
/* @__PURE__ */ jsx2(Label, { htmlFor: "eb-var-group", children: t("Group") }),
|
|
1560
|
+
/* @__PURE__ */ jsx2(
|
|
1653
1561
|
Input,
|
|
1654
1562
|
{
|
|
1655
1563
|
id: "eb-var-group",
|
|
@@ -1660,21 +1568,21 @@ function VariableDialog({ open, onOpenChange, variable = null, initialLabel = ""
|
|
|
1660
1568
|
placeholder: t(CUSTOM_GROUP)
|
|
1661
1569
|
}
|
|
1662
1570
|
),
|
|
1663
|
-
/* @__PURE__ */
|
|
1571
|
+
/* @__PURE__ */ jsx2("datalist", { id: "eb-var-groups", children: groups.map((g) => /* @__PURE__ */ jsx2("option", { value: g }, g)) })
|
|
1664
1572
|
] }),
|
|
1665
|
-
/* @__PURE__ */
|
|
1573
|
+
/* @__PURE__ */ jsxs2("div", { className: "eb:grid eb:grid-cols-2 eb:gap-3", children: [
|
|
1666
1574
|
field("sample", t("Sample value"), { maxLength: VARIABLE_LIMITS.sample, placeholder: "CMD-1042" }, t("Preview and tests")),
|
|
1667
1575
|
field("fallback", t("Value if empty"), { maxLength: VARIABLE_LIMITS.fallback }, t("When a recipient has none"))
|
|
1668
1576
|
] }),
|
|
1669
|
-
/* @__PURE__ */
|
|
1670
|
-
editing && /* @__PURE__ */
|
|
1671
|
-
/* @__PURE__ */
|
|
1577
|
+
/* @__PURE__ */ jsxs2("div", { className: "eb:flex eb:items-center eb:gap-2 eb:pt-1", children: [
|
|
1578
|
+
editing && /* @__PURE__ */ jsxs2(Button, { variant: "ghost", className: "eb:text-destructive eb:hover:text-destructive", onClick: remove, disabled: busy, children: [
|
|
1579
|
+
/* @__PURE__ */ jsx2(Trash2, {}),
|
|
1672
1580
|
t("Delete")
|
|
1673
1581
|
] }),
|
|
1674
|
-
/* @__PURE__ */
|
|
1675
|
-
/* @__PURE__ */
|
|
1676
|
-
/* @__PURE__ */
|
|
1677
|
-
busy && /* @__PURE__ */
|
|
1582
|
+
/* @__PURE__ */ jsxs2("div", { className: "eb:ml-auto eb:flex eb:gap-2", children: [
|
|
1583
|
+
/* @__PURE__ */ jsx2(Button, { variant: "outline", onClick: () => onOpenChange(false), disabled: busy, children: t("Cancel") }),
|
|
1584
|
+
/* @__PURE__ */ jsxs2(Button, { type: "submit", disabled: busy, children: [
|
|
1585
|
+
busy && /* @__PURE__ */ jsx2(Loader2, { className: "eb:animate-spin" }),
|
|
1678
1586
|
editing ? t("Save") : t("Create")
|
|
1679
1587
|
] })
|
|
1680
1588
|
] })
|
|
@@ -1685,199 +1593,137 @@ function VariableDialog({ open, onOpenChange, variable = null, initialLabel = ""
|
|
|
1685
1593
|
) });
|
|
1686
1594
|
}
|
|
1687
1595
|
|
|
1688
|
-
// src/features/variables/
|
|
1689
|
-
import {
|
|
1690
|
-
|
|
1691
|
-
|
|
1692
|
-
|
|
1693
|
-
const t =
|
|
1694
|
-
const [
|
|
1695
|
-
const [
|
|
1696
|
-
const
|
|
1697
|
-
const
|
|
1698
|
-
|
|
1699
|
-
|
|
1700
|
-
);
|
|
1701
|
-
|
|
1702
|
-
const
|
|
1703
|
-
const
|
|
1704
|
-
|
|
1705
|
-
(v) => !q || t(v.label).toLowerCase().includes(q) || v.key.toLowerCase().includes(q) || t(v.group).toLowerCase().includes(q)
|
|
1706
|
-
);
|
|
1707
|
-
}, [variables, query, filter, t]);
|
|
1708
|
-
const groups = useMemo2(() => [...new Set(matches.map((v) => v.group))], [matches]);
|
|
1709
|
-
const ordered = useMemo2(() => groups.flatMap((g) => matches.filter((v) => v.group === g)), [groups, matches]);
|
|
1710
|
-
useEffect2(() => setActive(0), [query]);
|
|
1711
|
-
useEffect2(() => {
|
|
1712
|
-
listRef.current?.querySelector('[data-active="true"]')?.scrollIntoView({ block: "nearest" });
|
|
1713
|
-
}, [active]);
|
|
1714
|
-
const onKeyDown = (e) => {
|
|
1715
|
-
if (e.key === "ArrowDown") {
|
|
1716
|
-
e.preventDefault();
|
|
1717
|
-
setActive((i) => Math.min(i + 1, ordered.length - 1));
|
|
1718
|
-
} else if (e.key === "ArrowUp") {
|
|
1719
|
-
e.preventDefault();
|
|
1720
|
-
setActive((i) => Math.max(i - 1, 0));
|
|
1721
|
-
} else if (e.key === "Enter") {
|
|
1722
|
-
e.preventDefault();
|
|
1723
|
-
if (ordered[active]) onPick(ordered[active]);
|
|
1724
|
-
else if (onCreate && query.trim()) onCreate(query.trim());
|
|
1725
|
-
}
|
|
1596
|
+
// src/features/variables/VariablesBar.jsx
|
|
1597
|
+
import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
1598
|
+
var BADGE = "eb:inline-flex eb:h-5.5 eb:items-center eb:gap-1 eb:rounded-full eb:border eb:border-border eb:bg-surface-75 eb:px-2 eb:text-[11px] eb:font-medium eb:text-foreground eb:transition-colors eb:outline-none eb:hover:border-primary/50 eb:hover:bg-primary/5 eb:hover:text-primary eb:focus-visible:ring-2 eb:focus-visible:ring-ring/50 eb:disabled:opacity-50";
|
|
1599
|
+
function VariablesBar({ context }) {
|
|
1600
|
+
const { editor, ready, variables, storage, canManageVariables, reloadVariables } = context;
|
|
1601
|
+
const { t, notify } = useUi();
|
|
1602
|
+
const [hovered, setHovered] = useState3(false);
|
|
1603
|
+
const [open, setOpen] = useState3(false);
|
|
1604
|
+
const [dialog, setDialog] = useState3(null);
|
|
1605
|
+
const listId = useId();
|
|
1606
|
+
if (!variables.length && !canManageVariables) return null;
|
|
1607
|
+
const expanded = hovered || open;
|
|
1608
|
+
const fields = variables.filter((v) => v.kind !== "link");
|
|
1609
|
+
const links = variables.filter((v) => v.kind === "link");
|
|
1610
|
+
const insert = (variable) => {
|
|
1611
|
+
const done = variable.kind === "link" ? editor.insertLink(tokenOf(variable), t("Unsubscribe")) : editor.insertText(tokenOf(variable));
|
|
1612
|
+
if (!done) notify("info", t("Select a text or a button first."));
|
|
1726
1613
|
};
|
|
1727
|
-
|
|
1728
|
-
|
|
1729
|
-
|
|
1730
|
-
|
|
1731
|
-
|
|
1732
|
-
{
|
|
1733
|
-
ref: searchRef,
|
|
1734
|
-
autoFocus: true,
|
|
1735
|
-
value: query,
|
|
1736
|
-
onChange: (e) => setQuery(e.target.value),
|
|
1737
|
-
onKeyDown,
|
|
1738
|
-
placeholder: t("Search variables\u2026"),
|
|
1739
|
-
"aria-label": t("Search variables\u2026"),
|
|
1740
|
-
"aria-keyshortcuts": SEARCH_KEYS,
|
|
1741
|
-
className: "eb:h-8 eb:w-full eb:rounded-md eb:bg-surface-75 eb:pr-14 eb:pl-7 eb:text-[13px] eb:text-foreground eb:outline-none eb:placeholder:text-foreground-muted eb:focus-visible:ring-2 eb:focus-visible:ring-ring/40"
|
|
1742
|
-
}
|
|
1743
|
-
),
|
|
1744
|
-
/* @__PURE__ */ jsx4(SearchKeyHint, { className: "eb:absolute eb:top-1/2 eb:right-4 eb:-translate-y-1/2" })
|
|
1745
|
-
] }),
|
|
1746
|
-
/* @__PURE__ */ jsxs4("div", { ref: listRef, role: "listbox", "aria-label": t("Variables"), className: "eb:min-h-0 eb:flex-1 eb:overflow-y-auto eb:p-1", children: [
|
|
1747
|
-
ordered.length === 0 && /* @__PURE__ */ jsxs4("div", { className: "eb:px-2 eb:py-3 eb:text-center eb:text-[12px] eb:text-foreground-muted", children: [
|
|
1748
|
-
/* @__PURE__ */ jsx4("p", { children: t("No variable found.") }),
|
|
1749
|
-
onCreate && query.trim() && /* @__PURE__ */ jsxs4(
|
|
1750
|
-
"button",
|
|
1751
|
-
{
|
|
1752
|
-
type: "button",
|
|
1753
|
-
onClick: () => onCreate(query.trim()),
|
|
1754
|
-
className: "eb:mt-2 eb:inline-flex eb:items-center eb:gap-1 eb:rounded-md eb:px-2 eb:py-1 eb:text-[12px] eb:font-medium eb:text-primary eb:hover:bg-accent",
|
|
1755
|
-
children: [
|
|
1756
|
-
/* @__PURE__ */ jsx4(Plus, { className: "eb:size-3.5" }),
|
|
1757
|
-
t("Create \u201C{name}\u201D", { name: query.trim() })
|
|
1758
|
-
]
|
|
1759
|
-
}
|
|
1760
|
-
)
|
|
1761
|
-
] }),
|
|
1762
|
-
groups.map((group) => /* @__PURE__ */ jsxs4("div", { role: "group", "aria-label": t(group), children: [
|
|
1763
|
-
/* @__PURE__ */ jsx4("p", { className: "eb:px-2 eb:pt-2 eb:pb-1 eb:text-[11px] eb:text-foreground-muted", children: t(group) }),
|
|
1764
|
-
ordered.filter((v) => v.group === group).map((variable) => {
|
|
1765
|
-
const index = ordered.indexOf(variable);
|
|
1766
|
-
return /* @__PURE__ */ jsxs4(
|
|
1767
|
-
"div",
|
|
1768
|
-
{
|
|
1769
|
-
"data-active": index === active,
|
|
1770
|
-
className: cn(
|
|
1771
|
-
"eb:group/var eb:flex eb:items-center eb:gap-1 eb:rounded-md",
|
|
1772
|
-
index === active ? "eb:bg-accent eb:text-accent-foreground" : "eb:hover:bg-accent/60"
|
|
1773
|
-
),
|
|
1774
|
-
onMouseEnter: () => setActive(index),
|
|
1775
|
-
children: [
|
|
1776
|
-
/* @__PURE__ */ jsxs4(
|
|
1777
|
-
"button",
|
|
1778
|
-
{
|
|
1779
|
-
type: "button",
|
|
1780
|
-
role: "option",
|
|
1781
|
-
"aria-selected": index === active,
|
|
1782
|
-
"data-variable": variable.key,
|
|
1783
|
-
onClick: () => onPick(variable),
|
|
1784
|
-
className: "eb:flex eb:min-w-0 eb:flex-1 eb:items-center eb:gap-2 eb:px-2 eb:py-1.5 eb:text-left eb:text-[13px]",
|
|
1785
|
-
title: variable.description || void 0,
|
|
1786
|
-
children: [
|
|
1787
|
-
/* @__PURE__ */ jsx4("span", { className: "eb:min-w-0 eb:flex-1 eb:truncate", children: variable.custom ? variable.label : t(variable.label) }),
|
|
1788
|
-
/* @__PURE__ */ jsx4("code", { className: "eb:shrink-0 eb:font-mono eb:text-[10.5px] eb:text-foreground-muted", children: tokenOf(variable) })
|
|
1789
|
-
]
|
|
1790
|
-
}
|
|
1791
|
-
),
|
|
1792
|
-
onEdit && variable.custom && /* @__PURE__ */ jsx4(
|
|
1793
|
-
"button",
|
|
1794
|
-
{
|
|
1795
|
-
type: "button",
|
|
1796
|
-
"aria-label": t("Edit \u201C{name}\u201D", { name: variable.label }),
|
|
1797
|
-
title: t("Edit"),
|
|
1798
|
-
onClick: () => onEdit(variable),
|
|
1799
|
-
className: "eb:mr-1 eb:flex eb:size-6 eb:shrink-0 eb:items-center eb:justify-center eb:rounded-md eb:text-foreground-muted eb:opacity-0 eb:group-hover/var:opacity-100 eb:hover:bg-background eb:hover:text-foreground eb:focus-visible:opacity-100",
|
|
1800
|
-
children: /* @__PURE__ */ jsx4(Pencil, { className: "eb:size-3" })
|
|
1801
|
-
}
|
|
1802
|
-
)
|
|
1803
|
-
]
|
|
1804
|
-
},
|
|
1805
|
-
variable.key
|
|
1806
|
-
);
|
|
1807
|
-
})
|
|
1808
|
-
] }, group))
|
|
1809
|
-
] }),
|
|
1810
|
-
onCreate && /* @__PURE__ */ jsx4("div", { className: "eb:border-t eb:border-border eb:p-1", children: /* @__PURE__ */ jsxs4(
|
|
1614
|
+
const badge = (variable) => {
|
|
1615
|
+
const link2 = variable.kind === "link";
|
|
1616
|
+
const label = variable.custom ? variable.label : t(variable.label);
|
|
1617
|
+
const editable = canManageVariables && variable.custom;
|
|
1618
|
+
const button2 = /* @__PURE__ */ jsxs3(
|
|
1811
1619
|
"button",
|
|
1812
1620
|
{
|
|
1813
1621
|
type: "button",
|
|
1814
|
-
|
|
1815
|
-
|
|
1622
|
+
disabled: !ready,
|
|
1623
|
+
"data-variable": variable.key,
|
|
1624
|
+
onMouseEnter: link2 ? void 0 : () => editor.previewText(tokenOf(variable)),
|
|
1625
|
+
onMouseLeave: link2 ? void 0 : () => editor.clearPreview(),
|
|
1626
|
+
onFocus: link2 ? void 0 : () => editor.previewText(tokenOf(variable)),
|
|
1627
|
+
onBlur: link2 ? void 0 : () => editor.clearPreview(),
|
|
1628
|
+
onClick: () => insert(variable),
|
|
1629
|
+
className: cn(BADGE, editable && "eb:rounded-r-none eb:border-r-0 eb:pr-1.5"),
|
|
1816
1630
|
children: [
|
|
1817
|
-
/* @__PURE__ */
|
|
1818
|
-
|
|
1631
|
+
link2 && /* @__PURE__ */ jsx3(Link2, { className: "eb:size-3", "aria-hidden": "true" }),
|
|
1632
|
+
label
|
|
1819
1633
|
]
|
|
1820
1634
|
}
|
|
1821
|
-
)
|
|
1822
|
-
|
|
1823
|
-
}
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
|
|
1827
|
-
|
|
1828
|
-
const { editor, ready, variables, storage, canManageVariables, reloadVariables } = context;
|
|
1829
|
-
const { t, notify } = useUi();
|
|
1830
|
-
const [open, setOpen] = useState5(false);
|
|
1831
|
-
const [dialog, setDialog] = useState5(null);
|
|
1832
|
-
const insert = (variable) => {
|
|
1833
|
-
setOpen(false);
|
|
1834
|
-
const done = variable.kind === "link" ? editor.insertLink(tokenOf(variable), t("Unsubscribe")) : editor.insertText(tokenOf(variable));
|
|
1835
|
-
if (!done) notify("info", t("Select a text or a button first."));
|
|
1836
|
-
return done;
|
|
1837
|
-
};
|
|
1838
|
-
const manage = canManageVariables ? {
|
|
1839
|
-
onCreate: (label) => {
|
|
1840
|
-
setOpen(false);
|
|
1841
|
-
setDialog({ variable: null, label });
|
|
1842
|
-
},
|
|
1843
|
-
onEdit: (variable) => {
|
|
1844
|
-
setOpen(false);
|
|
1845
|
-
setDialog({ variable, label: "" });
|
|
1846
|
-
}
|
|
1847
|
-
} : {};
|
|
1848
|
-
return /* @__PURE__ */ jsxs5(Fragment, { children: [
|
|
1849
|
-
/* @__PURE__ */ jsxs5(Popover, { open, onOpenChange: setOpen, children: [
|
|
1850
|
-
/* @__PURE__ */ jsx5(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsxs5(
|
|
1851
|
-
Button,
|
|
1852
|
-
{
|
|
1853
|
-
variant: "outline",
|
|
1854
|
-
size: "sm-compact",
|
|
1855
|
-
disabled: !ready,
|
|
1856
|
-
onPointerDown: (e) => e.pointerType === "mouse" && e.preventDefault(),
|
|
1857
|
-
children: [
|
|
1858
|
-
/* @__PURE__ */ jsx5(Braces3, {}),
|
|
1859
|
-
t("Variables")
|
|
1860
|
-
]
|
|
1861
|
-
}
|
|
1862
|
-
) }),
|
|
1863
|
-
/* @__PURE__ */ jsx5(
|
|
1864
|
-
PopoverContent,
|
|
1635
|
+
);
|
|
1636
|
+
const tip = /* @__PURE__ */ jsx3(TooltipWrap, { label: /* @__PURE__ */ jsx3("code", { className: "eb:font-mono", children: tokenOf(variable) }), delayDuration: 100, hoverable: false, children: button2 });
|
|
1637
|
+
if (!editable) return /* @__PURE__ */ jsx3(Fragment, { children: tip }, variable.key);
|
|
1638
|
+
return /* @__PURE__ */ jsxs3("span", { className: "eb:inline-flex", children: [
|
|
1639
|
+
tip,
|
|
1640
|
+
/* @__PURE__ */ jsx3(
|
|
1641
|
+
"button",
|
|
1865
1642
|
{
|
|
1866
|
-
|
|
1867
|
-
|
|
1868
|
-
|
|
1869
|
-
|
|
1870
|
-
|
|
1643
|
+
type: "button",
|
|
1644
|
+
"aria-label": t("Edit \u201C{name}\u201D", { name: variable.label }),
|
|
1645
|
+
title: t("Edit"),
|
|
1646
|
+
onClick: () => setDialog({ variable }),
|
|
1647
|
+
className: cn(BADGE, "eb:rounded-l-none eb:px-1.5 eb:text-foreground-muted"),
|
|
1648
|
+
children: /* @__PURE__ */ jsx3(Pencil, { className: "eb:size-2.5", "aria-hidden": "true" })
|
|
1871
1649
|
}
|
|
1872
1650
|
)
|
|
1873
|
-
] })
|
|
1874
|
-
|
|
1651
|
+
] }, variable.key);
|
|
1652
|
+
};
|
|
1653
|
+
return /* @__PURE__ */ jsxs3("div", { className: "eb:relative eb:z-10 eb:h-6 eb:shrink-0", "data-testid": "eb-variables-bar", children: [
|
|
1654
|
+
/* @__PURE__ */ jsxs3(
|
|
1655
|
+
"div",
|
|
1656
|
+
{
|
|
1657
|
+
className: cn(
|
|
1658
|
+
"eb:absolute eb:inset-x-0 eb:top-0 eb:border-b eb:border-border eb:bg-background eb:transition-shadow",
|
|
1659
|
+
expanded && "eb:shadow-md"
|
|
1660
|
+
),
|
|
1661
|
+
"data-expanded": expanded,
|
|
1662
|
+
onMouseEnter: () => setHovered(true),
|
|
1663
|
+
onMouseLeave: () => setHovered(false),
|
|
1664
|
+
onKeyDown: (e) => e.key === "Escape" && open && setOpen(false),
|
|
1665
|
+
onMouseDown: (e) => e.preventDefault(),
|
|
1666
|
+
children: [
|
|
1667
|
+
/* @__PURE__ */ jsxs3(
|
|
1668
|
+
"button",
|
|
1669
|
+
{
|
|
1670
|
+
type: "button",
|
|
1671
|
+
"aria-expanded": expanded,
|
|
1672
|
+
"aria-controls": listId,
|
|
1673
|
+
onClick: () => setOpen((value) => !value),
|
|
1674
|
+
className: "eb:flex eb:h-6 eb:w-full eb:items-center eb:gap-1.5 eb:px-3 eb:text-[11px] eb:font-medium eb:text-foreground-lighter eb:outline-none eb:hover:text-foreground eb:focus-visible:ring-2 eb:focus-visible:ring-ring/40 eb:focus-visible:ring-inset",
|
|
1675
|
+
children: [
|
|
1676
|
+
/* @__PURE__ */ jsx3(Braces2, { className: "eb:size-3", "aria-hidden": "true" }),
|
|
1677
|
+
t("Email provider variables"),
|
|
1678
|
+
fields.length > 0 && /* @__PURE__ */ jsx3("span", { className: "eb:text-foreground-muted", children: fields.length }),
|
|
1679
|
+
/* @__PURE__ */ jsx3(ChevronDown, { className: cn("eb:ml-auto eb:size-3.5 eb:transition-transform", expanded && "eb:rotate-180"), "aria-hidden": "true" })
|
|
1680
|
+
]
|
|
1681
|
+
}
|
|
1682
|
+
),
|
|
1683
|
+
/* @__PURE__ */ jsx3(
|
|
1684
|
+
"div",
|
|
1685
|
+
{
|
|
1686
|
+
id: listId,
|
|
1687
|
+
className: cn(
|
|
1688
|
+
"eb:grid eb:transition-[grid-template-rows,visibility] eb:duration-200 eb:ease-out",
|
|
1689
|
+
expanded ? "eb:visible eb:grid-rows-[1fr]" : "eb:invisible eb:grid-rows-[0fr]"
|
|
1690
|
+
),
|
|
1691
|
+
children: /* @__PURE__ */ jsx3("div", { className: "eb:min-h-0 eb:overflow-hidden", children: /* @__PURE__ */ jsxs3(
|
|
1692
|
+
"div",
|
|
1693
|
+
{
|
|
1694
|
+
role: "group",
|
|
1695
|
+
"aria-label": t("Email provider variables"),
|
|
1696
|
+
className: "eb:flex eb:max-h-24 eb:flex-wrap eb:items-center eb:gap-1 eb:overflow-y-auto eb:px-3 eb:pt-0.5 eb:pb-2",
|
|
1697
|
+
children: [
|
|
1698
|
+
fields.map(badge),
|
|
1699
|
+
!fields.length && /* @__PURE__ */ jsx3("span", { className: "eb:text-[11px] eb:text-foreground-muted", children: t("No variables yet.") }),
|
|
1700
|
+
canManageVariables && /* @__PURE__ */ jsx3(TooltipWrap, { label: t("New variable"), delayDuration: 100, hoverable: false, children: /* @__PURE__ */ jsx3(
|
|
1701
|
+
"button",
|
|
1702
|
+
{
|
|
1703
|
+
type: "button",
|
|
1704
|
+
disabled: !ready,
|
|
1705
|
+
"aria-label": t("New variable"),
|
|
1706
|
+
onClick: () => setDialog({}),
|
|
1707
|
+
className: cn(BADGE, "eb:w-5.5 eb:justify-center eb:border-dashed eb:bg-transparent eb:px-0 eb:text-primary"),
|
|
1708
|
+
children: /* @__PURE__ */ jsx3(Plus, { className: "eb:size-3.5", "aria-hidden": "true" })
|
|
1709
|
+
}
|
|
1710
|
+
) }),
|
|
1711
|
+
links.length > 0 && /* @__PURE__ */ jsx3("span", { className: "eb:mx-1 eb:h-3.5 eb:w-px eb:bg-border", "aria-hidden": "true" }),
|
|
1712
|
+
links.map(badge)
|
|
1713
|
+
]
|
|
1714
|
+
}
|
|
1715
|
+
) })
|
|
1716
|
+
}
|
|
1717
|
+
)
|
|
1718
|
+
]
|
|
1719
|
+
}
|
|
1720
|
+
),
|
|
1721
|
+
canManageVariables && /* @__PURE__ */ jsx3(
|
|
1875
1722
|
VariableDialog,
|
|
1876
1723
|
{
|
|
1877
1724
|
open: Boolean(dialog),
|
|
1878
1725
|
onOpenChange: (next) => !next && setDialog(null),
|
|
1879
1726
|
variable: dialog?.variable || null,
|
|
1880
|
-
initialLabel: dialog?.label || "",
|
|
1881
1727
|
variables,
|
|
1882
1728
|
storage,
|
|
1883
1729
|
onSaved: async (variable, created) => {
|
|
@@ -1906,7 +1752,6 @@ var variablesFeature = {
|
|
|
1906
1752
|
id: "variables",
|
|
1907
1753
|
label: "Variables",
|
|
1908
1754
|
order: 10,
|
|
1909
|
-
toolbar: VariablesMenu,
|
|
1910
1755
|
canvasBar: VariablesBar
|
|
1911
1756
|
};
|
|
1912
1757
|
|
|
@@ -1920,9 +1765,9 @@ var previewFeature = {
|
|
|
1920
1765
|
};
|
|
1921
1766
|
|
|
1922
1767
|
// src/features/test-email/TestEmailButton.jsx
|
|
1923
|
-
import { useState as
|
|
1768
|
+
import { useState as useState4 } from "react";
|
|
1924
1769
|
import { CheckCircle2, Loader2 as Loader22, Send, XCircle } from "lucide-react";
|
|
1925
|
-
import { Fragment as Fragment2, jsx as
|
|
1770
|
+
import { Fragment as Fragment2, jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
1926
1771
|
var KEY = "best-email-builder:test-addresses";
|
|
1927
1772
|
function readAddresses(fallback) {
|
|
1928
1773
|
try {
|
|
@@ -1940,10 +1785,10 @@ function rememberAddresses(value) {
|
|
|
1940
1785
|
function TestEmailButton({ context }) {
|
|
1941
1786
|
const { editor, ready, meta, storage, testRecipients = [] } = context;
|
|
1942
1787
|
const { t, notify } = useUi();
|
|
1943
|
-
const [open, setOpen] =
|
|
1944
|
-
const [addresses, setAddresses] =
|
|
1945
|
-
const [sending, setSending] =
|
|
1946
|
-
const [report, setReport] =
|
|
1788
|
+
const [open, setOpen] = useState4(false);
|
|
1789
|
+
const [addresses, setAddresses] = useState4("");
|
|
1790
|
+
const [sending, setSending] = useState4(false);
|
|
1791
|
+
const [report, setReport] = useState4(null);
|
|
1947
1792
|
const emails = addresses.split(/[,;\s]+/).map((a) => a.trim()).filter(Boolean);
|
|
1948
1793
|
const send = async () => {
|
|
1949
1794
|
if (!emails.length) return;
|
|
@@ -1966,8 +1811,8 @@ function TestEmailButton({ context }) {
|
|
|
1966
1811
|
setSending(false);
|
|
1967
1812
|
}
|
|
1968
1813
|
};
|
|
1969
|
-
return /* @__PURE__ */
|
|
1970
|
-
/* @__PURE__ */
|
|
1814
|
+
return /* @__PURE__ */ jsxs4(Fragment2, { children: [
|
|
1815
|
+
/* @__PURE__ */ jsxs4(
|
|
1971
1816
|
Button,
|
|
1972
1817
|
{
|
|
1973
1818
|
variant: "outline",
|
|
@@ -1979,22 +1824,22 @@ function TestEmailButton({ context }) {
|
|
|
1979
1824
|
setOpen(true);
|
|
1980
1825
|
},
|
|
1981
1826
|
children: [
|
|
1982
|
-
/* @__PURE__ */
|
|
1827
|
+
/* @__PURE__ */ jsx4(Send, {}),
|
|
1983
1828
|
t("Test")
|
|
1984
1829
|
]
|
|
1985
1830
|
}
|
|
1986
1831
|
),
|
|
1987
|
-
/* @__PURE__ */
|
|
1988
|
-
/* @__PURE__ */
|
|
1989
|
-
/* @__PURE__ */
|
|
1990
|
-
/* @__PURE__ */
|
|
1991
|
-
/* @__PURE__ */
|
|
1992
|
-
/* @__PURE__ */
|
|
1832
|
+
/* @__PURE__ */ jsx4(Dialog, { open, onOpenChange: setOpen, children: /* @__PURE__ */ jsxs4(DialogContent, { className: "eb:sm:max-w-[460px]", children: [
|
|
1833
|
+
/* @__PURE__ */ jsx4(DialogHeader, { children: /* @__PURE__ */ jsxs4("div", { className: "eb:flex eb:items-center eb:gap-3", children: [
|
|
1834
|
+
/* @__PURE__ */ jsx4("span", { className: "eb:flex eb:size-10 eb:shrink-0 eb:items-center eb:justify-center eb:rounded-lg2 eb:bg-primary/10 eb:text-primary", children: /* @__PURE__ */ jsx4(Send, { className: "eb:size-5" }) }),
|
|
1835
|
+
/* @__PURE__ */ jsxs4("div", { children: [
|
|
1836
|
+
/* @__PURE__ */ jsx4(DialogTitle, { className: "eb:text-base eb:font-normal", children: t("Send a test") }),
|
|
1837
|
+
/* @__PURE__ */ jsx4(DialogDescription, { className: "eb:text-[13px]", children: t("The email on screen, to reviewers\u2019 addresses.") })
|
|
1993
1838
|
] })
|
|
1994
1839
|
] }) }),
|
|
1995
|
-
/* @__PURE__ */
|
|
1996
|
-
/* @__PURE__ */
|
|
1997
|
-
/* @__PURE__ */
|
|
1840
|
+
/* @__PURE__ */ jsxs4("div", { className: "eb:space-y-2", children: [
|
|
1841
|
+
/* @__PURE__ */ jsx4(Label, { htmlFor: "eb-test-addresses", children: t("Addresses") }),
|
|
1842
|
+
/* @__PURE__ */ jsx4(
|
|
1998
1843
|
Textarea,
|
|
1999
1844
|
{
|
|
2000
1845
|
id: "eb-test-addresses",
|
|
@@ -2005,20 +1850,20 @@ function TestEmailButton({ context }) {
|
|
|
2005
1850
|
}
|
|
2006
1851
|
)
|
|
2007
1852
|
] }),
|
|
2008
|
-
report && /* @__PURE__ */
|
|
2009
|
-
report.results.map((r) => /* @__PURE__ */
|
|
2010
|
-
r.ok ? /* @__PURE__ */
|
|
2011
|
-
/* @__PURE__ */
|
|
1853
|
+
report && /* @__PURE__ */ jsxs4("ul", { className: "eb:space-y-1", "aria-label": t("Results"), children: [
|
|
1854
|
+
report.results.map((r) => /* @__PURE__ */ jsxs4("li", { className: "eb:flex eb:items-start eb:gap-1.5 eb:text-[12px] eb:text-foreground", children: [
|
|
1855
|
+
r.ok ? /* @__PURE__ */ jsx4(CheckCircle2, { className: "eb:mt-0.5 eb:size-3.5 eb:shrink-0 eb:text-success", "aria-hidden": "true" }) : /* @__PURE__ */ jsx4(XCircle, { className: "eb:mt-0.5 eb:size-3.5 eb:shrink-0 eb:text-destructive", "aria-hidden": "true" }),
|
|
1856
|
+
/* @__PURE__ */ jsxs4("span", { className: "eb:min-w-0 eb:break-all", children: [
|
|
2012
1857
|
r.email,
|
|
2013
1858
|
r.error ? ` \u2014 ${r.error}` : ""
|
|
2014
1859
|
] })
|
|
2015
1860
|
] }, r.email)),
|
|
2016
|
-
report.invalid.map((email2) => /* @__PURE__ */
|
|
1861
|
+
report.invalid.map((email2) => /* @__PURE__ */ jsx4("li", { className: "eb:text-[12px] eb:text-foreground-muted", children: t("Ignored (invalid): {email}", { email: email2 }) }, `invalid-${email2}`))
|
|
2017
1862
|
] }),
|
|
2018
|
-
/* @__PURE__ */
|
|
2019
|
-
/* @__PURE__ */
|
|
2020
|
-
/* @__PURE__ */
|
|
2021
|
-
sending ? /* @__PURE__ */
|
|
1863
|
+
/* @__PURE__ */ jsxs4("div", { className: "eb:flex eb:justify-end eb:gap-2", children: [
|
|
1864
|
+
/* @__PURE__ */ jsx4(Button, { variant: "outline", onClick: () => setOpen(false), children: t("Close") }),
|
|
1865
|
+
/* @__PURE__ */ jsxs4(Button, { onClick: send, disabled: sending || !emails.length, children: [
|
|
1866
|
+
sending ? /* @__PURE__ */ jsx4(Loader22, { className: "eb:animate-spin" }) : /* @__PURE__ */ jsx4(Send, {}),
|
|
2022
1867
|
t("Send")
|
|
2023
1868
|
] })
|
|
2024
1869
|
] })
|
|
@@ -2036,9 +1881,9 @@ var testEmailFeature = {
|
|
|
2036
1881
|
};
|
|
2037
1882
|
|
|
2038
1883
|
// src/features/templates/TemplateGallery.jsx
|
|
2039
|
-
import { useEffect as
|
|
2040
|
-
import { LayoutTemplate, Search
|
|
2041
|
-
import { Fragment as Fragment3, jsx as
|
|
1884
|
+
import { useEffect as useEffect2, useMemo as useMemo2, useState as useState5 } from "react";
|
|
1885
|
+
import { LayoutTemplate, Search, X } from "lucide-react";
|
|
1886
|
+
import { Fragment as Fragment3, jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
2042
1887
|
var THUMBNAIL_SCALE = 0.38;
|
|
2043
1888
|
var EMAIL_WIDTH = 600;
|
|
2044
1889
|
var FADE_LINE = "eb:block eb:overflow-hidden eb:whitespace-nowrap eb:[mask-image:linear-gradient(to_right,#000_calc(100%-2.5rem),transparent)]";
|
|
@@ -2046,12 +1891,12 @@ var NO_SCROLLBAR = "<style>html,body{overflow:hidden!important;scrollbar-width:n
|
|
|
2046
1891
|
var withoutScrollbar = (html) => !html ? html : /<\/head>/i.test(html) ? html.replace(/<\/head>/i, `${NO_SCROLLBAR}</head>`) : NO_SCROLLBAR + html;
|
|
2047
1892
|
var fold = (text) => String(text || "").normalize("NFD").replace(/[̀-ͯ]/g, "").toLowerCase();
|
|
2048
1893
|
function Thumbnail({ html, title }) {
|
|
2049
|
-
return /* @__PURE__ */
|
|
1894
|
+
return /* @__PURE__ */ jsx5(
|
|
2050
1895
|
"div",
|
|
2051
1896
|
{
|
|
2052
1897
|
className: "eb:relative eb:h-[220px] eb:shrink-0 eb:overflow-hidden eb:rounded-md eb:border eb:border-border eb:bg-surface-75",
|
|
2053
1898
|
"aria-hidden": "true",
|
|
2054
|
-
children: /* @__PURE__ */
|
|
1899
|
+
children: /* @__PURE__ */ jsx5(
|
|
2055
1900
|
"iframe",
|
|
2056
1901
|
{
|
|
2057
1902
|
title,
|
|
@@ -2073,11 +1918,11 @@ function Thumbnail({ html, title }) {
|
|
|
2073
1918
|
function TemplateGalleryButton({ context }) {
|
|
2074
1919
|
const { editor, ready, templates, isDirty, markDirty, template, setMeta } = context;
|
|
2075
1920
|
const { t, confirm, notify } = useUi();
|
|
2076
|
-
const [open, setOpen] =
|
|
2077
|
-
const [query, setQuery] =
|
|
2078
|
-
const [chosen, setChosen] =
|
|
2079
|
-
const [rendered, setRendered] =
|
|
2080
|
-
|
|
1921
|
+
const [open, setOpen] = useState5(false);
|
|
1922
|
+
const [query, setQuery] = useState5("");
|
|
1923
|
+
const [chosen, setChosen] = useState5(() => /* @__PURE__ */ new Set());
|
|
1924
|
+
const [rendered, setRendered] = useState5({});
|
|
1925
|
+
useEffect2(() => {
|
|
2081
1926
|
if (!open || !ready) return void 0;
|
|
2082
1927
|
let alive = true;
|
|
2083
1928
|
Promise.all(
|
|
@@ -2095,12 +1940,12 @@ function TemplateGalleryButton({ context }) {
|
|
|
2095
1940
|
alive = false;
|
|
2096
1941
|
};
|
|
2097
1942
|
}, [open, ready, templates, editor]);
|
|
2098
|
-
const cards =
|
|
1943
|
+
const cards = useMemo2(
|
|
2099
1944
|
() => open ? templates.map((entry) => ({ ...entry, tags: entry.tags || [], html: rendered[entry.id] ?? "" })) : [],
|
|
2100
1945
|
[open, templates, rendered]
|
|
2101
1946
|
);
|
|
2102
|
-
const tags =
|
|
2103
|
-
const shown =
|
|
1947
|
+
const tags = useMemo2(() => [...new Set(templates.flatMap((entry) => entry.tags || []))], [templates]);
|
|
1948
|
+
const shown = useMemo2(() => {
|
|
2104
1949
|
const q = fold(query.trim());
|
|
2105
1950
|
return cards.filter(
|
|
2106
1951
|
(card) => (chosen.size === 0 || card.tags.some((tag) => chosen.has(tag))) && (!q || [card.name, card.description, ...card.tags].some((field) => fold(field).includes(q)))
|
|
@@ -2133,8 +1978,8 @@ function TemplateGalleryButton({ context }) {
|
|
|
2133
1978
|
setOpen(false);
|
|
2134
1979
|
notify("success", t("\u201C{name}\u201D template applied.", { name: entry.name }));
|
|
2135
1980
|
};
|
|
2136
|
-
return /* @__PURE__ */
|
|
2137
|
-
/* @__PURE__ */
|
|
1981
|
+
return /* @__PURE__ */ jsxs5(Fragment3, { children: [
|
|
1982
|
+
/* @__PURE__ */ jsxs5(
|
|
2138
1983
|
Button,
|
|
2139
1984
|
{
|
|
2140
1985
|
variant: "outline",
|
|
@@ -2146,12 +1991,12 @@ function TemplateGalleryButton({ context }) {
|
|
|
2146
1991
|
setOpen(true);
|
|
2147
1992
|
},
|
|
2148
1993
|
children: [
|
|
2149
|
-
/* @__PURE__ */
|
|
1994
|
+
/* @__PURE__ */ jsx5(LayoutTemplate, {}),
|
|
2150
1995
|
t("Templates")
|
|
2151
1996
|
]
|
|
2152
1997
|
}
|
|
2153
1998
|
),
|
|
2154
|
-
/* @__PURE__ */
|
|
1999
|
+
/* @__PURE__ */ jsx5(Dialog, { open, onOpenChange: setOpen, children: /* @__PURE__ */ jsxs5(
|
|
2155
2000
|
DialogContent,
|
|
2156
2001
|
{
|
|
2157
2002
|
className: "eb:flex eb:h-[min(48rem,calc(100dvh-2rem))] eb:flex-col eb:sm:max-w-4xl",
|
|
@@ -2163,14 +2008,14 @@ function TemplateGalleryButton({ context }) {
|
|
|
2163
2008
|
}
|
|
2164
2009
|
},
|
|
2165
2010
|
children: [
|
|
2166
|
-
/* @__PURE__ */
|
|
2167
|
-
/* @__PURE__ */
|
|
2168
|
-
/* @__PURE__ */
|
|
2011
|
+
/* @__PURE__ */ jsxs5(DialogHeader, { children: [
|
|
2012
|
+
/* @__PURE__ */ jsx5(DialogTitle, { children: t("Start from a template") }),
|
|
2013
|
+
/* @__PURE__ */ jsx5(DialogDescription, { children: t("Picking a template replaces the email being edited.") })
|
|
2169
2014
|
] }),
|
|
2170
|
-
/* @__PURE__ */
|
|
2171
|
-
/* @__PURE__ */
|
|
2172
|
-
/* @__PURE__ */
|
|
2173
|
-
/* @__PURE__ */
|
|
2015
|
+
/* @__PURE__ */ jsxs5("div", { className: "eb:flex eb:shrink-0 eb:flex-col eb:gap-3", children: [
|
|
2016
|
+
/* @__PURE__ */ jsxs5("div", { className: "eb:relative", children: [
|
|
2017
|
+
/* @__PURE__ */ jsx5(Search, { className: "eb:pointer-events-none eb:absolute eb:top-1/2 eb:left-2.5 eb:size-3.5 eb:-translate-y-1/2 eb:text-foreground-muted" }),
|
|
2018
|
+
/* @__PURE__ */ jsx5(
|
|
2174
2019
|
Input,
|
|
2175
2020
|
{
|
|
2176
2021
|
type: "search",
|
|
@@ -2181,20 +2026,20 @@ function TemplateGalleryButton({ context }) {
|
|
|
2181
2026
|
className: "eb:pr-8 eb:pl-8 eb:[&::-webkit-search-cancel-button]:hidden"
|
|
2182
2027
|
}
|
|
2183
2028
|
),
|
|
2184
|
-
query && /* @__PURE__ */
|
|
2029
|
+
query && /* @__PURE__ */ jsx5(
|
|
2185
2030
|
"button",
|
|
2186
2031
|
{
|
|
2187
2032
|
type: "button",
|
|
2188
2033
|
onClick: () => setQuery(""),
|
|
2189
2034
|
"aria-label": t("Clear the search"),
|
|
2190
2035
|
className: "eb:absolute eb:top-1/2 eb:right-2.5 eb:-translate-y-1/2 eb:cursor-pointer",
|
|
2191
|
-
children: /* @__PURE__ */
|
|
2036
|
+
children: /* @__PURE__ */ jsx5(X, { className: "eb:size-3.5 eb:text-foreground-muted eb:hover:text-foreground" })
|
|
2192
2037
|
}
|
|
2193
2038
|
)
|
|
2194
2039
|
] }),
|
|
2195
|
-
tags.length > 0 && /* @__PURE__ */
|
|
2196
|
-
/* @__PURE__ */
|
|
2197
|
-
/* @__PURE__ */
|
|
2040
|
+
tags.length > 0 && /* @__PURE__ */ jsxs5("div", { className: "eb:flex eb:items-start eb:gap-1.5", children: [
|
|
2041
|
+
/* @__PURE__ */ jsx5("div", { role: "group", "aria-label": t("Filter by tag"), className: "eb:flex eb:min-w-0 eb:flex-1 eb:flex-wrap eb:gap-1.5", "data-testid": "eb-gallery-tags", children: tags.map((tag) => /* @__PURE__ */ jsx5(FilterChip, { pressed: chosen.has(tag), onClick: () => toggle(tag), children: tag }, tag)) }),
|
|
2042
|
+
/* @__PURE__ */ jsx5(
|
|
2198
2043
|
FilterReset,
|
|
2199
2044
|
{
|
|
2200
2045
|
label: t("Clear the tag filters"),
|
|
@@ -2205,7 +2050,7 @@ function TemplateGalleryButton({ context }) {
|
|
|
2205
2050
|
)
|
|
2206
2051
|
] })
|
|
2207
2052
|
] }),
|
|
2208
|
-
/* @__PURE__ */
|
|
2053
|
+
/* @__PURE__ */ jsx5("div", { className: "eb:-m-1 eb:min-h-0 eb:flex-1 eb:overflow-y-auto eb:p-1", children: shown.length === 0 ? /* @__PURE__ */ jsx5("p", { className: "eb:m-0 eb:py-10 eb:text-center eb:text-[13px] eb:text-foreground-muted", children: t("Nothing matches \u201C{query}\u201D.", { query: query.trim() }) }) : /* @__PURE__ */ jsx5("ul", { className: "eb:m-0 eb:grid eb:list-none eb:grid-cols-1 eb:gap-3 eb:p-0 eb:sm:grid-cols-2 eb:md:grid-cols-3", children: shown.map((card) => /* @__PURE__ */ jsx5("li", { className: "eb:min-w-0", children: /* @__PURE__ */ jsxs5(
|
|
2209
2054
|
"button",
|
|
2210
2055
|
{
|
|
2211
2056
|
type: "button",
|
|
@@ -2214,10 +2059,10 @@ function TemplateGalleryButton({ context }) {
|
|
|
2214
2059
|
title: card.description,
|
|
2215
2060
|
className: "eb:group eb:flex eb:h-full eb:w-full eb:flex-col eb:gap-2 eb:rounded-lg2 eb:border eb:border-border eb:bg-background eb:p-2 eb:text-left eb:transition-colors eb:hover:border-primary/50 eb:hover:bg-primary/5 eb:focus-visible:outline-none eb:focus-visible:ring-2 eb:focus-visible:ring-ring/50",
|
|
2216
2061
|
children: [
|
|
2217
|
-
/* @__PURE__ */
|
|
2218
|
-
/* @__PURE__ */
|
|
2219
|
-
/* @__PURE__ */
|
|
2220
|
-
/* @__PURE__ */
|
|
2062
|
+
/* @__PURE__ */ jsx5(Thumbnail, { html: card.html, title: card.name }),
|
|
2063
|
+
/* @__PURE__ */ jsxs5("span", { className: "eb:block eb:min-w-0 eb:px-1", children: [
|
|
2064
|
+
/* @__PURE__ */ jsx5("span", { className: cn(FADE_LINE, "eb:text-[13px] eb:font-medium eb:text-foreground"), children: card.name }),
|
|
2065
|
+
/* @__PURE__ */ jsx5("span", { className: cn(FADE_LINE, "eb:text-[12px] eb:text-foreground-muted"), children: card.description })
|
|
2221
2066
|
] })
|
|
2222
2067
|
]
|
|
2223
2068
|
}
|
|
@@ -2238,14 +2083,14 @@ var templatesFeature = {
|
|
|
2238
2083
|
};
|
|
2239
2084
|
|
|
2240
2085
|
// src/features/brand/BrandButton.jsx
|
|
2241
|
-
import { useEffect as
|
|
2086
|
+
import { useEffect as useEffect3, useRef, useState as useState7 } from "react";
|
|
2242
2087
|
import { Globe, ImageOff, Loader2 as Loader23, Palette, Trash2 as Trash22, TriangleAlert, Upload, WandSparkles } from "lucide-react";
|
|
2243
2088
|
|
|
2244
2089
|
// src/ui/primitives/color-picker.jsx
|
|
2245
|
-
import { useState as
|
|
2090
|
+
import { useState as useState6 } from "react";
|
|
2246
2091
|
import { HexAlphaColorPicker } from "react-colorful";
|
|
2247
2092
|
import { Check } from "lucide-react";
|
|
2248
|
-
import { jsx as
|
|
2093
|
+
import { jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
2249
2094
|
var PRESET_COLORS = [
|
|
2250
2095
|
"#8B5CF6",
|
|
2251
2096
|
"#A78BFA",
|
|
@@ -2276,20 +2121,20 @@ function normalizeHex(v) {
|
|
|
2276
2121
|
}
|
|
2277
2122
|
function ColorPicker({ value, onChange, id, className, placeholder = "" }) {
|
|
2278
2123
|
const t = useT();
|
|
2279
|
-
const [open, setOpen] =
|
|
2124
|
+
const [open, setOpen] = useState6(false);
|
|
2280
2125
|
const valid = isValidHex(value);
|
|
2281
2126
|
const normalized = valid ? normalizeHex(value) : "";
|
|
2282
2127
|
const emit = (hex) => onChange(normalizeHex(hex));
|
|
2283
|
-
return /* @__PURE__ */
|
|
2284
|
-
/* @__PURE__ */
|
|
2285
|
-
/* @__PURE__ */
|
|
2128
|
+
return /* @__PURE__ */ jsxs6("div", { className: cn("eb:flex eb:gap-2", className), children: [
|
|
2129
|
+
/* @__PURE__ */ jsxs6(Popover, { open, onOpenChange: setOpen, children: [
|
|
2130
|
+
/* @__PURE__ */ jsx6(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsx6(
|
|
2286
2131
|
"button",
|
|
2287
2132
|
{
|
|
2288
2133
|
type: "button",
|
|
2289
2134
|
"aria-label": t("Pick a colour"),
|
|
2290
2135
|
title: t("Pick a colour"),
|
|
2291
2136
|
className: "eb-checker eb:relative eb:h-8 eb:w-[44px] eb:shrink-0 eb:overflow-hidden eb:rounded-lg2 eb:shadow-sm eb:ring-1 eb:ring-black/10 eb:ring-inset eb:transition-transform eb:hover:scale-[1.04]",
|
|
2292
|
-
children: /* @__PURE__ */
|
|
2137
|
+
children: /* @__PURE__ */ jsx6(
|
|
2293
2138
|
"span",
|
|
2294
2139
|
{
|
|
2295
2140
|
className: "eb:absolute eb:inset-0",
|
|
@@ -2299,18 +2144,18 @@ function ColorPicker({ value, onChange, id, className, placeholder = "" }) {
|
|
|
2299
2144
|
)
|
|
2300
2145
|
}
|
|
2301
2146
|
) }),
|
|
2302
|
-
/* @__PURE__ */
|
|
2303
|
-
/* @__PURE__ */
|
|
2304
|
-
/* @__PURE__ */
|
|
2305
|
-
/* @__PURE__ */
|
|
2147
|
+
/* @__PURE__ */ jsxs6(PopoverContent, { align: "start", className: "eb:w-64 eb:space-y-3 eb:p-3", children: [
|
|
2148
|
+
/* @__PURE__ */ jsx6("div", { className: "eb-picker", children: /* @__PURE__ */ jsx6(HexAlphaColorPicker, { color: valid ? value : FALLBACK_COLOR, onChange: emit }) }),
|
|
2149
|
+
/* @__PURE__ */ jsxs6("div", { className: "eb:flex eb:items-center eb:gap-2", children: [
|
|
2150
|
+
/* @__PURE__ */ jsx6(
|
|
2306
2151
|
"span",
|
|
2307
2152
|
{
|
|
2308
2153
|
className: "eb-checker eb:relative eb:size-8 eb:shrink-0 eb:overflow-hidden eb:rounded-lg2 eb:ring-1 eb:ring-black/10 eb:ring-inset",
|
|
2309
2154
|
"aria-hidden": true,
|
|
2310
|
-
children: /* @__PURE__ */
|
|
2155
|
+
children: /* @__PURE__ */ jsx6("span", { className: "eb:absolute eb:inset-0", style: { backgroundColor: valid ? value : "transparent" } })
|
|
2311
2156
|
}
|
|
2312
2157
|
),
|
|
2313
|
-
/* @__PURE__ */
|
|
2158
|
+
/* @__PURE__ */ jsx6(
|
|
2314
2159
|
Input,
|
|
2315
2160
|
{
|
|
2316
2161
|
value: value || "",
|
|
@@ -2322,7 +2167,7 @@ function ColorPicker({ value, onChange, id, className, placeholder = "" }) {
|
|
|
2322
2167
|
}
|
|
2323
2168
|
)
|
|
2324
2169
|
] }),
|
|
2325
|
-
/* @__PURE__ */
|
|
2170
|
+
/* @__PURE__ */ jsx6("div", { className: "eb:grid eb:grid-cols-6 eb:gap-1.5 eb:border-t eb:border-border eb:pt-2.5", children: PRESET_COLORS.map((c) => /* @__PURE__ */ jsx6(
|
|
2326
2171
|
"button",
|
|
2327
2172
|
{
|
|
2328
2173
|
type: "button",
|
|
@@ -2331,13 +2176,13 @@ function ColorPicker({ value, onChange, id, className, placeholder = "" }) {
|
|
|
2331
2176
|
"aria-label": c,
|
|
2332
2177
|
className: "eb:relative eb:size-6 eb:rounded-lg2 eb:ring-1 eb:ring-black/10 eb:ring-inset eb:transition-transform eb:hover:scale-110",
|
|
2333
2178
|
style: { backgroundColor: c },
|
|
2334
|
-
children: normalized === c && /* @__PURE__ */
|
|
2179
|
+
children: normalized === c && /* @__PURE__ */ jsx6(Check, { className: "eb:absolute eb:inset-0 eb:m-auto eb:size-3.5 eb:text-white eb:drop-shadow-[0_1px_1px_rgba(0,0,0,0.6)]" })
|
|
2335
2180
|
},
|
|
2336
2181
|
c
|
|
2337
2182
|
)) })
|
|
2338
2183
|
] })
|
|
2339
2184
|
] }),
|
|
2340
|
-
/* @__PURE__ */
|
|
2185
|
+
/* @__PURE__ */ jsx6(
|
|
2341
2186
|
Input,
|
|
2342
2187
|
{
|
|
2343
2188
|
id,
|
|
@@ -2352,7 +2197,7 @@ function ColorPicker({ value, onChange, id, className, placeholder = "" }) {
|
|
|
2352
2197
|
}
|
|
2353
2198
|
|
|
2354
2199
|
// src/features/brand/BrandButton.jsx
|
|
2355
|
-
import { Fragment as Fragment4, jsx as
|
|
2200
|
+
import { Fragment as Fragment4, jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
2356
2201
|
var toForm = (brand) => ({
|
|
2357
2202
|
name: brand.name || "",
|
|
2358
2203
|
logoUrl: brand.logoUrl || "",
|
|
@@ -2366,31 +2211,31 @@ var isLogoUrl = (url) => !url || url.startsWith("/") || /^https:\/\//i.test(url)
|
|
|
2366
2211
|
function BrandButton({ context }) {
|
|
2367
2212
|
const { editor, ready, storage, brand, setBrand, hostSender } = context;
|
|
2368
2213
|
const { t, notify } = useUi();
|
|
2369
|
-
const [open, setOpen] =
|
|
2370
|
-
const [form, setForm] =
|
|
2371
|
-
const [applyNow, setApplyNow] =
|
|
2372
|
-
const [reading, setReading] =
|
|
2373
|
-
const [readError, setReadError] =
|
|
2374
|
-
const [notes, setNotes] =
|
|
2214
|
+
const [open, setOpen] = useState7(false);
|
|
2215
|
+
const [form, setForm] = useState7(() => toForm(brand || {}));
|
|
2216
|
+
const [applyNow, setApplyNow] = useState7(true);
|
|
2217
|
+
const [reading, setReading] = useState7(false);
|
|
2218
|
+
const [readError, setReadError] = useState7("");
|
|
2219
|
+
const [notes, setNotes] = useState7(
|
|
2375
2220
|
/** @type {string[]} */
|
|
2376
2221
|
[]
|
|
2377
2222
|
);
|
|
2378
|
-
const [uploading, setUploading] =
|
|
2379
|
-
const [saving, setSaving] =
|
|
2380
|
-
const [error, setError] =
|
|
2381
|
-
const abortRef =
|
|
2223
|
+
const [uploading, setUploading] = useState7(false);
|
|
2224
|
+
const [saving, setSaving] = useState7(false);
|
|
2225
|
+
const [error, setError] = useState7("");
|
|
2226
|
+
const abortRef = useRef(
|
|
2382
2227
|
/** @type {AbortController|null} */
|
|
2383
2228
|
null
|
|
2384
2229
|
);
|
|
2385
|
-
const fileRef =
|
|
2230
|
+
const fileRef = useRef(
|
|
2386
2231
|
/** @type {HTMLInputElement|null} */
|
|
2387
2232
|
null
|
|
2388
2233
|
);
|
|
2389
2234
|
const canRead = typeof storage.importBrand === "function";
|
|
2390
2235
|
const canUpload = typeof storage.uploadImage === "function";
|
|
2391
|
-
|
|
2236
|
+
useEffect3(() => () => abortRef.current?.abort(), []);
|
|
2392
2237
|
const logoUrl = form.logoUrl;
|
|
2393
|
-
|
|
2238
|
+
useEffect3(() => {
|
|
2394
2239
|
if (!logoUrl || !isLogoUrl(logoUrl)) return void 0;
|
|
2395
2240
|
let live = true;
|
|
2396
2241
|
const image = new Image();
|
|
@@ -2499,11 +2344,11 @@ function BrandButton({ context }) {
|
|
|
2499
2344
|
}
|
|
2500
2345
|
};
|
|
2501
2346
|
const swatch = brand?.primary;
|
|
2502
|
-
return /* @__PURE__ */
|
|
2503
|
-
/* @__PURE__ */
|
|
2504
|
-
/* @__PURE__ */
|
|
2347
|
+
return /* @__PURE__ */ jsxs7(Fragment4, { children: [
|
|
2348
|
+
/* @__PURE__ */ jsxs7(Button, { variant: "outline", size: "sm-compact", disabled: !ready, onClick: openDialog, "data-testid": "eb-brand-button", children: [
|
|
2349
|
+
/* @__PURE__ */ jsx7(Palette, {}),
|
|
2505
2350
|
t("Brand"),
|
|
2506
|
-
swatch && /* @__PURE__ */
|
|
2351
|
+
swatch && /* @__PURE__ */ jsx7(
|
|
2507
2352
|
"span",
|
|
2508
2353
|
{
|
|
2509
2354
|
"aria-hidden": "true",
|
|
@@ -2512,18 +2357,18 @@ function BrandButton({ context }) {
|
|
|
2512
2357
|
}
|
|
2513
2358
|
)
|
|
2514
2359
|
] }),
|
|
2515
|
-
/* @__PURE__ */
|
|
2360
|
+
/* @__PURE__ */ jsx7(Dialog, { open, onOpenChange: close, children: /* @__PURE__ */ jsxs7(
|
|
2516
2361
|
DialogContent,
|
|
2517
2362
|
{
|
|
2518
2363
|
className: "eb:flex eb:max-h-[calc(100dvh-2rem)] eb:flex-col eb:gap-0 eb:p-0 eb:sm:max-w-2xl",
|
|
2519
2364
|
"data-testid": "eb-brand",
|
|
2520
2365
|
children: [
|
|
2521
|
-
/* @__PURE__ */
|
|
2522
|
-
/* @__PURE__ */
|
|
2523
|
-
/* @__PURE__ */
|
|
2366
|
+
/* @__PURE__ */ jsxs7(DialogHeader, { className: "eb:gap-1.5 eb:px-6 eb:pt-6 eb:pb-4", children: [
|
|
2367
|
+
/* @__PURE__ */ jsx7(DialogTitle, { children: t("Your brand") }),
|
|
2368
|
+
/* @__PURE__ */ jsx7(DialogDescription, { children: t("Set it once: new emails, templates and blocks take your logo, colors and font.") })
|
|
2524
2369
|
] }),
|
|
2525
|
-
/* @__PURE__ */
|
|
2526
|
-
canRead && /* @__PURE__ */
|
|
2370
|
+
/* @__PURE__ */ jsxs7("div", { className: "eb:min-h-0 eb:flex-1 eb:overflow-y-auto eb:border-y eb:border-border eb:px-6 eb:py-5", children: [
|
|
2371
|
+
canRead && /* @__PURE__ */ jsxs7(
|
|
2527
2372
|
"form",
|
|
2528
2373
|
{
|
|
2529
2374
|
className: "eb:mb-5 eb:flex eb:flex-col eb:gap-1.5",
|
|
@@ -2532,11 +2377,11 @@ function BrandButton({ context }) {
|
|
|
2532
2377
|
readWebsite();
|
|
2533
2378
|
},
|
|
2534
2379
|
children: [
|
|
2535
|
-
/* @__PURE__ */
|
|
2536
|
-
/* @__PURE__ */
|
|
2537
|
-
/* @__PURE__ */
|
|
2538
|
-
/* @__PURE__ */
|
|
2539
|
-
/* @__PURE__ */
|
|
2380
|
+
/* @__PURE__ */ jsx7(Label, { htmlFor: "eb-brand-website", children: t("Your website") }),
|
|
2381
|
+
/* @__PURE__ */ jsxs7("div", { className: "eb:flex eb:flex-col eb:gap-2 eb:sm:flex-row", children: [
|
|
2382
|
+
/* @__PURE__ */ jsxs7("div", { className: "eb:relative eb:min-w-0 eb:flex-1", children: [
|
|
2383
|
+
/* @__PURE__ */ jsx7(Globe, { className: "eb:pointer-events-none eb:absolute eb:top-1/2 eb:left-2.5 eb:size-3.5 eb:-translate-y-1/2 eb:text-foreground-muted", "aria-hidden": "true" }),
|
|
2384
|
+
/* @__PURE__ */ jsx7(
|
|
2540
2385
|
Input,
|
|
2541
2386
|
{
|
|
2542
2387
|
id: "eb-brand-website",
|
|
@@ -2550,28 +2395,28 @@ function BrandButton({ context }) {
|
|
|
2550
2395
|
}
|
|
2551
2396
|
)
|
|
2552
2397
|
] }),
|
|
2553
|
-
/* @__PURE__ */
|
|
2554
|
-
reading ? /* @__PURE__ */
|
|
2398
|
+
/* @__PURE__ */ jsxs7(Button, { type: "submit", size: "sm", disabled: reading || !form.website.trim(), children: [
|
|
2399
|
+
reading ? /* @__PURE__ */ jsx7(Loader23, { className: "eb:animate-spin" }) : /* @__PURE__ */ jsx7(WandSparkles, {}),
|
|
2555
2400
|
t("Get my brand")
|
|
2556
2401
|
] })
|
|
2557
2402
|
] }),
|
|
2558
|
-
reading ? /* @__PURE__ */
|
|
2559
|
-
/* @__PURE__ */
|
|
2403
|
+
reading ? /* @__PURE__ */ jsx7("p", { role: "status", className: "eb:text-[12px] eb:text-foreground-muted", children: t("Reading the website\u2026") }) : readError ? /* @__PURE__ */ jsxs7("p", { role: "alert", className: "eb:flex eb:items-start eb:gap-1.5 eb:text-[12px] eb:text-destructive", children: [
|
|
2404
|
+
/* @__PURE__ */ jsx7(TriangleAlert, { className: "eb:mt-px eb:size-3.5 eb:shrink-0", "aria-hidden": "true" }),
|
|
2560
2405
|
readError
|
|
2561
|
-
] }) : notes.length ? /* @__PURE__ */
|
|
2406
|
+
] }) : notes.length ? /* @__PURE__ */ jsx7("ul", { className: "eb:flex eb:flex-col eb:gap-1 eb:text-[12px] eb:text-foreground-muted", "data-testid": "eb-brand-notes", children: notes.map((note) => /* @__PURE__ */ jsx7("li", { children: t(BRAND_NOTES[note]) }, note)) }) : /* @__PURE__ */ jsx7("p", { className: "eb:text-[12px] eb:text-foreground-muted", children: t("The logo, colors and font are read from it. You can change them before saving.") })
|
|
2562
2407
|
]
|
|
2563
2408
|
}
|
|
2564
2409
|
),
|
|
2565
|
-
/* @__PURE__ */
|
|
2566
|
-
/* @__PURE__ */
|
|
2567
|
-
/* @__PURE__ */
|
|
2568
|
-
/* @__PURE__ */
|
|
2569
|
-
/* @__PURE__ */
|
|
2570
|
-
/* @__PURE__ */
|
|
2571
|
-
/* @__PURE__ */
|
|
2572
|
-
/* @__PURE__ */
|
|
2573
|
-
canUpload && /* @__PURE__ */
|
|
2574
|
-
/* @__PURE__ */
|
|
2410
|
+
/* @__PURE__ */ jsxs7("div", { className: "eb:grid eb:gap-6 eb:sm:grid-cols-[minmax(0,1fr)_220px]", children: [
|
|
2411
|
+
/* @__PURE__ */ jsxs7("fieldset", { disabled: reading, className: "eb:m-0 eb:flex eb:min-w-0 eb:flex-col eb:gap-4 eb:border-0 eb:p-0", children: [
|
|
2412
|
+
/* @__PURE__ */ jsx7("legend", { className: "eb:sr-only", children: t("Your brand") }),
|
|
2413
|
+
/* @__PURE__ */ jsxs7("div", { className: "eb:flex eb:flex-col eb:gap-1.5", children: [
|
|
2414
|
+
/* @__PURE__ */ jsx7(Label, { htmlFor: "eb-brand-logo", children: t("Logo") }),
|
|
2415
|
+
/* @__PURE__ */ jsxs7("div", { className: "eb:flex eb:items-center eb:gap-3", children: [
|
|
2416
|
+
/* @__PURE__ */ jsx7("div", { className: "eb-checker eb:flex eb:h-12 eb:w-24 eb:shrink-0 eb:items-center eb:justify-center eb:overflow-hidden eb:rounded-lg2 eb:ring-1 eb:ring-border eb:ring-inset", children: form.logoUrl && !logoInvalid ? /* @__PURE__ */ jsx7("img", { src: form.logoUrl, alt: t("Logo"), className: "eb:max-h-10 eb:max-w-20 eb:object-contain" }) : /* @__PURE__ */ jsx7(ImageOff, { className: "eb:size-4 eb:text-foreground-muted", "aria-hidden": "true" }) }),
|
|
2417
|
+
/* @__PURE__ */ jsxs7("div", { className: "eb:flex eb:flex-wrap eb:gap-2", children: [
|
|
2418
|
+
canUpload && /* @__PURE__ */ jsxs7(Fragment4, { children: [
|
|
2419
|
+
/* @__PURE__ */ jsx7(
|
|
2575
2420
|
"input",
|
|
2576
2421
|
{
|
|
2577
2422
|
ref: fileRef,
|
|
@@ -2584,18 +2429,18 @@ function BrandButton({ context }) {
|
|
|
2584
2429
|
onChange: pickLogo
|
|
2585
2430
|
}
|
|
2586
2431
|
),
|
|
2587
|
-
/* @__PURE__ */
|
|
2588
|
-
uploading ? /* @__PURE__ */
|
|
2432
|
+
/* @__PURE__ */ jsxs7(Button, { type: "button", variant: "outline", size: "sm", disabled: uploading, onClick: () => fileRef.current?.click(), children: [
|
|
2433
|
+
uploading ? /* @__PURE__ */ jsx7(Loader23, { className: "eb:animate-spin" }) : /* @__PURE__ */ jsx7(Upload, {}),
|
|
2589
2434
|
t("Upload")
|
|
2590
2435
|
] })
|
|
2591
2436
|
] }),
|
|
2592
|
-
form.logoUrl && /* @__PURE__ */
|
|
2593
|
-
/* @__PURE__ */
|
|
2437
|
+
form.logoUrl && /* @__PURE__ */ jsxs7(Button, { type: "button", variant: "ghost", size: "sm", onClick: () => set({ logoUrl: "", logoWidth: 0 }), children: [
|
|
2438
|
+
/* @__PURE__ */ jsx7(Trash22, {}),
|
|
2594
2439
|
t("Remove")
|
|
2595
2440
|
] })
|
|
2596
2441
|
] })
|
|
2597
2442
|
] }),
|
|
2598
|
-
/* @__PURE__ */
|
|
2443
|
+
/* @__PURE__ */ jsx7(
|
|
2599
2444
|
Input,
|
|
2600
2445
|
{
|
|
2601
2446
|
id: "eb-brand-logo",
|
|
@@ -2606,11 +2451,11 @@ function BrandButton({ context }) {
|
|
|
2606
2451
|
className: "eb:text-[12px]"
|
|
2607
2452
|
}
|
|
2608
2453
|
),
|
|
2609
|
-
logoInvalid && /* @__PURE__ */
|
|
2454
|
+
logoInvalid && /* @__PURE__ */ jsx7("p", { className: "eb:text-[12px] eb:text-destructive", children: t("An https:// address is required") })
|
|
2610
2455
|
] }),
|
|
2611
|
-
/* @__PURE__ */
|
|
2612
|
-
/* @__PURE__ */
|
|
2613
|
-
/* @__PURE__ */
|
|
2456
|
+
/* @__PURE__ */ jsxs7("div", { className: "eb:flex eb:flex-col eb:gap-1.5", children: [
|
|
2457
|
+
/* @__PURE__ */ jsx7(Label, { htmlFor: "eb-brand-name", children: t("Company name") }),
|
|
2458
|
+
/* @__PURE__ */ jsx7(
|
|
2614
2459
|
Input,
|
|
2615
2460
|
{
|
|
2616
2461
|
id: "eb-brand-name",
|
|
@@ -2621,36 +2466,36 @@ function BrandButton({ context }) {
|
|
|
2621
2466
|
}
|
|
2622
2467
|
)
|
|
2623
2468
|
] }),
|
|
2624
|
-
/* @__PURE__ */
|
|
2625
|
-
/* @__PURE__ */
|
|
2626
|
-
/* @__PURE__ */
|
|
2627
|
-
/* @__PURE__ */
|
|
2628
|
-
/* @__PURE__ */
|
|
2469
|
+
/* @__PURE__ */ jsxs7("div", { className: "eb:grid eb:gap-4 eb:sm:grid-cols-2", children: [
|
|
2470
|
+
/* @__PURE__ */ jsxs7("div", { className: "eb:flex eb:flex-col eb:gap-1.5", children: [
|
|
2471
|
+
/* @__PURE__ */ jsx7(Label, { htmlFor: "eb-brand-primary", children: t("Main color") }),
|
|
2472
|
+
/* @__PURE__ */ jsx7(ColorPicker, { id: "eb-brand-primary", value: form.primary, onChange: (primary) => set({ primary }), placeholder: PALETTE.navy }),
|
|
2473
|
+
/* @__PURE__ */ jsx7("p", { className: "eb:text-[11px] eb:text-foreground-muted", children: t("Buttons, banners, links") })
|
|
2629
2474
|
] }),
|
|
2630
|
-
/* @__PURE__ */
|
|
2631
|
-
/* @__PURE__ */
|
|
2632
|
-
/* @__PURE__ */
|
|
2475
|
+
/* @__PURE__ */ jsxs7("div", { className: "eb:flex eb:flex-col eb:gap-1.5", children: [
|
|
2476
|
+
/* @__PURE__ */ jsx7(Label, { htmlFor: "eb-brand-heading", children: t("Headings color") }),
|
|
2477
|
+
/* @__PURE__ */ jsx7(ColorPicker, { id: "eb-brand-heading", value: form.heading, onChange: (heading2) => set({ heading: heading2 }), placeholder: PALETTE.navy })
|
|
2633
2478
|
] })
|
|
2634
2479
|
] }),
|
|
2635
|
-
/* @__PURE__ */
|
|
2636
|
-
/* @__PURE__ */
|
|
2637
|
-
/* @__PURE__ */
|
|
2638
|
-
/* @__PURE__ */
|
|
2639
|
-
/* @__PURE__ */
|
|
2480
|
+
/* @__PURE__ */ jsxs7("div", { className: "eb:flex eb:flex-col eb:gap-1.5", children: [
|
|
2481
|
+
/* @__PURE__ */ jsx7(Label, { htmlFor: "eb-brand-font", children: t("Font") }),
|
|
2482
|
+
/* @__PURE__ */ jsxs7(Select, { value: form.font || FONT_STACK, onValueChange: (font) => set({ font }), children: [
|
|
2483
|
+
/* @__PURE__ */ jsx7(SelectTrigger, { id: "eb-brand-font", children: /* @__PURE__ */ jsx7(SelectValue, {}) }),
|
|
2484
|
+
/* @__PURE__ */ jsx7(SelectContent, { children: FONT_OPTIONS.map((option) => /* @__PURE__ */ jsx7(SelectItem, { value: option.value, children: /* @__PURE__ */ jsx7("span", { style: { fontFamily: option.value }, children: option.label }) }, option.value)) })
|
|
2640
2485
|
] })
|
|
2641
2486
|
] })
|
|
2642
2487
|
] }),
|
|
2643
|
-
/* @__PURE__ */
|
|
2488
|
+
/* @__PURE__ */ jsx7(BrandPreview, { brand: draft, fallbackName: hostSender?.name || t("Your company") })
|
|
2644
2489
|
] })
|
|
2645
2490
|
] }),
|
|
2646
|
-
/* @__PURE__ */
|
|
2647
|
-
error && /* @__PURE__ */
|
|
2648
|
-
/* @__PURE__ */
|
|
2491
|
+
/* @__PURE__ */ jsxs7("div", { className: "eb:flex eb:flex-col eb:gap-3 eb:px-6 eb:pt-3 eb:pb-6", children: [
|
|
2492
|
+
error && /* @__PURE__ */ jsxs7("p", { role: "alert", className: "eb:flex eb:items-start eb:gap-1.5 eb:text-[12px] eb:text-destructive", children: [
|
|
2493
|
+
/* @__PURE__ */ jsx7(TriangleAlert, { className: "eb:mt-px eb:size-3.5 eb:shrink-0", "aria-hidden": "true" }),
|
|
2649
2494
|
error
|
|
2650
2495
|
] }),
|
|
2651
|
-
/* @__PURE__ */
|
|
2652
|
-
/* @__PURE__ */
|
|
2653
|
-
/* @__PURE__ */
|
|
2496
|
+
/* @__PURE__ */ jsxs7("div", { className: "eb:flex eb:flex-col eb:gap-3 eb:sm:flex-row eb:sm:items-center", children: [
|
|
2497
|
+
/* @__PURE__ */ jsxs7("label", { className: "eb:flex eb:cursor-pointer eb:items-center eb:gap-2 eb:text-[13px] eb:sm:mr-auto", children: [
|
|
2498
|
+
/* @__PURE__ */ jsx7(
|
|
2654
2499
|
"input",
|
|
2655
2500
|
{
|
|
2656
2501
|
type: "checkbox",
|
|
@@ -2662,10 +2507,10 @@ function BrandButton({ context }) {
|
|
|
2662
2507
|
),
|
|
2663
2508
|
t("Also apply to this email")
|
|
2664
2509
|
] }),
|
|
2665
|
-
/* @__PURE__ */
|
|
2666
|
-
/* @__PURE__ */
|
|
2667
|
-
/* @__PURE__ */
|
|
2668
|
-
saving && /* @__PURE__ */
|
|
2510
|
+
/* @__PURE__ */ jsxs7("div", { className: "eb:flex eb:justify-end eb:gap-2", children: [
|
|
2511
|
+
/* @__PURE__ */ jsx7(Button, { variant: "outline", size: "sm", onClick: () => close(false), children: t("Cancel") }),
|
|
2512
|
+
/* @__PURE__ */ jsxs7(Button, { size: "sm", onClick: save, disabled: saving || reading || uploading || logoInvalid, "data-testid": "eb-brand-save", children: [
|
|
2513
|
+
saving && /* @__PURE__ */ jsx7(Loader23, { className: "eb:animate-spin" }),
|
|
2669
2514
|
t("Save")
|
|
2670
2515
|
] })
|
|
2671
2516
|
] })
|
|
@@ -2684,30 +2529,30 @@ function BrandPreview({ brand, fallbackName }) {
|
|
|
2684
2529
|
const link2 = contrastRatio(primary, PALETTE.white) >= 3 ? primary : heading2;
|
|
2685
2530
|
const font = brand.font || FONT_STACK;
|
|
2686
2531
|
const name = brand.name || fallbackName;
|
|
2687
|
-
return /* @__PURE__ */
|
|
2688
|
-
/* @__PURE__ */
|
|
2689
|
-
/* @__PURE__ */
|
|
2532
|
+
return /* @__PURE__ */ jsxs7("div", { className: "eb:flex eb:flex-col eb:gap-1.5", children: [
|
|
2533
|
+
/* @__PURE__ */ jsx7("span", { className: "eb:text-[12px] eb:font-medium eb:text-foreground-muted", children: t("Preview") }),
|
|
2534
|
+
/* @__PURE__ */ jsx7(
|
|
2690
2535
|
"div",
|
|
2691
2536
|
{
|
|
2692
2537
|
className: "eb:overflow-hidden eb:rounded-lg2 eb:p-2.5 eb:ring-1 eb:ring-border",
|
|
2693
2538
|
style: { backgroundColor: PALETTE.canvas, fontFamily: font },
|
|
2694
2539
|
"data-testid": "eb-brand-preview",
|
|
2695
2540
|
"aria-hidden": "true",
|
|
2696
|
-
children: /* @__PURE__ */
|
|
2697
|
-
/* @__PURE__ */
|
|
2698
|
-
/* @__PURE__ */
|
|
2699
|
-
/* @__PURE__ */
|
|
2700
|
-
/* @__PURE__ */
|
|
2541
|
+
children: /* @__PURE__ */ jsxs7("div", { className: "eb:overflow-hidden eb:rounded-[3px]", style: { backgroundColor: PALETTE.white }, children: [
|
|
2542
|
+
/* @__PURE__ */ jsx7("div", { className: "eb:px-3 eb:pt-3 eb:pb-2", children: brand.logoUrl ? /* @__PURE__ */ jsx7("img", { src: brand.logoUrl, alt: "", style: { width: Math.round((brand.logoWidth || 140) * 0.45), maxWidth: "100%" } }) : /* @__PURE__ */ jsx7("span", { className: "eb:text-[11px] eb:font-bold", style: { color: PALETTE.ink }, children: name }) }),
|
|
2543
|
+
/* @__PURE__ */ jsxs7("div", { className: "eb:px-3 eb:py-4 eb:text-center", style: { backgroundColor: primary, color: onPrimary }, children: [
|
|
2544
|
+
/* @__PURE__ */ jsx7("div", { className: "eb:text-[13px] eb:leading-tight eb:font-bold", children: t("Big news") }),
|
|
2545
|
+
/* @__PURE__ */ jsx7("div", { className: "eb:mt-1 eb:text-[9px] eb:opacity-90", children: t("A short line under the title.") })
|
|
2701
2546
|
] }),
|
|
2702
|
-
/* @__PURE__ */
|
|
2703
|
-
/* @__PURE__ */
|
|
2704
|
-
/* @__PURE__ */
|
|
2547
|
+
/* @__PURE__ */ jsxs7("div", { className: "eb:flex eb:flex-col eb:gap-1.5 eb:px-3 eb:py-3", children: [
|
|
2548
|
+
/* @__PURE__ */ jsx7("div", { className: "eb:text-[11px] eb:leading-tight eb:font-bold", style: { color: heading2 }, children: t("A headline") }),
|
|
2549
|
+
/* @__PURE__ */ jsxs7("div", { className: "eb:text-[9px] eb:leading-snug", style: { color: PALETTE.ink }, children: [
|
|
2705
2550
|
t("Some text, with"),
|
|
2706
2551
|
" ",
|
|
2707
|
-
/* @__PURE__ */
|
|
2552
|
+
/* @__PURE__ */ jsx7("span", { className: "eb:underline", style: { color: link2 }, children: t("a link") }),
|
|
2708
2553
|
"."
|
|
2709
2554
|
] }),
|
|
2710
|
-
/* @__PURE__ */
|
|
2555
|
+
/* @__PURE__ */ jsx7(
|
|
2711
2556
|
"span",
|
|
2712
2557
|
{
|
|
2713
2558
|
className: cn("eb:mt-1 eb:self-start eb:rounded-[3px] eb:px-2.5 eb:py-1 eb:text-[9px] eb:font-bold"),
|
|
@@ -2716,7 +2561,7 @@ function BrandPreview({ brand, fallbackName }) {
|
|
|
2716
2561
|
}
|
|
2717
2562
|
)
|
|
2718
2563
|
] }),
|
|
2719
|
-
/* @__PURE__ */
|
|
2564
|
+
/* @__PURE__ */ jsx7("div", { className: "eb:px-3 eb:pb-2.5 eb:text-center eb:text-[7px]", style: { color: "#6d7484" }, children: name })
|
|
2720
2565
|
] })
|
|
2721
2566
|
}
|
|
2722
2567
|
)
|
|
@@ -4672,14 +4517,14 @@ function createDefaultEmailEditor(options = {}) {
|
|
|
4672
4517
|
}
|
|
4673
4518
|
|
|
4674
4519
|
// src/ui/EmailCanvas.jsx
|
|
4675
|
-
import { useEffect as
|
|
4676
|
-
import { jsx as
|
|
4520
|
+
import { useEffect as useEffect4, useRef as useRef2 } from "react";
|
|
4521
|
+
import { jsx as jsx8 } from "react/jsx-runtime";
|
|
4677
4522
|
function EmailCanvas({ editor, document = null, onReady, onError, className }) {
|
|
4678
|
-
const ref =
|
|
4679
|
-
const documentRef =
|
|
4680
|
-
const callbacks =
|
|
4523
|
+
const ref = useRef2(null);
|
|
4524
|
+
const documentRef = useRef2(document);
|
|
4525
|
+
const callbacks = useRef2({ onReady, onError });
|
|
4681
4526
|
callbacks.current = { onReady, onError };
|
|
4682
|
-
|
|
4527
|
+
useEffect4(() => {
|
|
4683
4528
|
if (!editor || !ref.current) return void 0;
|
|
4684
4529
|
let alive = true;
|
|
4685
4530
|
editor.initialize({ container: ref.current, document: documentRef.current }).then(() => {
|
|
@@ -4692,15 +4537,15 @@ function EmailCanvas({ editor, document = null, onReady, onError, className }) {
|
|
|
4692
4537
|
editor.destroy();
|
|
4693
4538
|
};
|
|
4694
4539
|
}, [editor]);
|
|
4695
|
-
return /* @__PURE__ */
|
|
4540
|
+
return /* @__PURE__ */ jsx8("div", { ref, className: cn("eb-canvas eb:h-full eb:w-full", className), "data-testid": "eb-canvas" });
|
|
4696
4541
|
}
|
|
4697
4542
|
|
|
4698
4543
|
// src/ui/EmailBuilder.jsx
|
|
4699
|
-
import { useCallback as useCallback3, useEffect as useEffect10, useMemo as useMemo5, useRef as useRef8, useState as
|
|
4700
|
-
import { AlertTriangle, Loader2 as Loader25, Plus as
|
|
4544
|
+
import { useCallback as useCallback3, useEffect as useEffect10, useMemo as useMemo5, useRef as useRef8, useState as useState13 } from "react";
|
|
4545
|
+
import { AlertTriangle, Loader2 as Loader25, Plus as Plus3, Save, SlidersHorizontal } from "lucide-react";
|
|
4701
4546
|
|
|
4702
4547
|
// src/features/brand/useBrand.js
|
|
4703
|
-
import { useCallback, useEffect as
|
|
4548
|
+
import { useCallback, useEffect as useEffect5, useState as useState8 } from "react";
|
|
4704
4549
|
var entries = /* @__PURE__ */ new WeakMap();
|
|
4705
4550
|
function brandOf(storage) {
|
|
4706
4551
|
let entry = entries.get(storage);
|
|
@@ -4721,8 +4566,8 @@ function brandOf(storage) {
|
|
|
4721
4566
|
return created;
|
|
4722
4567
|
}
|
|
4723
4568
|
function useBrand(storage) {
|
|
4724
|
-
const [brand, setState] =
|
|
4725
|
-
|
|
4569
|
+
const [brand, setState] = useState8(() => brandOf(storage).value);
|
|
4570
|
+
useEffect5(() => {
|
|
4726
4571
|
const entry = brandOf(storage);
|
|
4727
4572
|
setState(entry.value);
|
|
4728
4573
|
entry.listeners.add(setState);
|
|
@@ -4849,8 +4694,8 @@ import {
|
|
|
4849
4694
|
Square,
|
|
4850
4695
|
Type
|
|
4851
4696
|
} from "lucide-react";
|
|
4852
|
-
import { useRef as
|
|
4853
|
-
import { jsx as
|
|
4697
|
+
import { useRef as useRef3 } from "react";
|
|
4698
|
+
import { jsx as jsx9, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
4854
4699
|
var DRAG_THRESHOLD = 5;
|
|
4855
4700
|
var ICONS2 = {
|
|
4856
4701
|
Heading,
|
|
@@ -4875,8 +4720,8 @@ var GROUPS2 = [
|
|
|
4875
4720
|
function BlockTile({ block, disabled, onAdd, onDragStart }) {
|
|
4876
4721
|
const t = useT();
|
|
4877
4722
|
const Icon = ICONS2[block.icon] || Square;
|
|
4878
|
-
const press =
|
|
4879
|
-
return /* @__PURE__ */
|
|
4723
|
+
const press = useRef3(null);
|
|
4724
|
+
return /* @__PURE__ */ jsx9(TooltipWrap, { label: block.description ? t(block.description) : block.description, children: /* @__PURE__ */ jsxs8(
|
|
4880
4725
|
"button",
|
|
4881
4726
|
{
|
|
4882
4727
|
type: "button",
|
|
@@ -4916,8 +4761,8 @@ function BlockTile({ block, disabled, onAdd, onDragStart }) {
|
|
|
4916
4761
|
disabled && "eb:cursor-not-allowed eb:opacity-50"
|
|
4917
4762
|
),
|
|
4918
4763
|
children: [
|
|
4919
|
-
/* @__PURE__ */
|
|
4920
|
-
/* @__PURE__ */
|
|
4764
|
+
/* @__PURE__ */ jsx9(Icon, { className: "eb:h-5 eb:w-5 eb:text-foreground-lighter", "aria-hidden": "true" }),
|
|
4765
|
+
/* @__PURE__ */ jsx9("span", { className: "eb:text-[12px] eb:text-foreground", children: t(block.label) })
|
|
4921
4766
|
]
|
|
4922
4767
|
}
|
|
4923
4768
|
) });
|
|
@@ -4925,12 +4770,12 @@ function BlockTile({ block, disabled, onAdd, onDragStart }) {
|
|
|
4925
4770
|
function BlockSidebar({ editor, disabled = false, onAdded }) {
|
|
4926
4771
|
const t = useT();
|
|
4927
4772
|
const blocks = editor.listBlocks();
|
|
4928
|
-
return /* @__PURE__ */
|
|
4773
|
+
return /* @__PURE__ */ jsx9("aside", { "aria-label": t("Blocks"), className: "eb:min-h-0 eb:space-y-5 eb:overflow-y-auto eb:border-r eb:border-border eb:p-3", children: GROUPS2.map((group) => {
|
|
4929
4774
|
const items = blocks.filter((b) => b.kind === group.kind && b.mjml);
|
|
4930
4775
|
if (!items.length) return null;
|
|
4931
|
-
return /* @__PURE__ */
|
|
4932
|
-
/* @__PURE__ */
|
|
4933
|
-
/* @__PURE__ */
|
|
4776
|
+
return /* @__PURE__ */ jsxs8("section", { className: "eb:space-y-2", children: [
|
|
4777
|
+
/* @__PURE__ */ jsx9(SectionLabel, { children: t(group.title) }),
|
|
4778
|
+
/* @__PURE__ */ jsx9("div", { className: "eb:grid eb:grid-cols-2 eb:gap-2", children: items.map((block) => /* @__PURE__ */ jsx9(
|
|
4934
4779
|
BlockTile,
|
|
4935
4780
|
{
|
|
4936
4781
|
block,
|
|
@@ -4948,15 +4793,15 @@ function BlockSidebar({ editor, disabled = false, onAdded }) {
|
|
|
4948
4793
|
|
|
4949
4794
|
// src/ui/ElementActions.jsx
|
|
4950
4795
|
import { ArrowDown, ArrowLeft, ArrowRight, ArrowUp, Copy, Trash2 as Trash23 } from "lucide-react";
|
|
4951
|
-
import { jsx as
|
|
4796
|
+
import { jsx as jsx10, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
4952
4797
|
function ElementActions({ editor, selection }) {
|
|
4953
4798
|
const t = useT();
|
|
4954
4799
|
if (!selection || !selection.canRemove && !selection.canDuplicate) return null;
|
|
4955
4800
|
const horizontal = selection.tagName === "mj-column";
|
|
4956
4801
|
const [Prev, Next] = horizontal ? [ArrowLeft, ArrowRight] : [ArrowUp, ArrowDown];
|
|
4957
4802
|
const [prevLabel, nextLabel] = horizontal ? [t("Move left"), t("Move right")] : [t("Move up"), t("Move down")];
|
|
4958
|
-
return /* @__PURE__ */
|
|
4959
|
-
/* @__PURE__ */
|
|
4803
|
+
return /* @__PURE__ */ jsxs9("div", { className: "eb:flex eb:items-center eb:gap-0.5 eb:border-b eb:border-border eb:px-3 eb:py-1.5", role: "toolbar", "aria-label": t("Element actions"), children: [
|
|
4804
|
+
/* @__PURE__ */ jsx10(TooltipWrap, { label: prevLabel, children: /* @__PURE__ */ jsx10(
|
|
4960
4805
|
Button,
|
|
4961
4806
|
{
|
|
4962
4807
|
variant: "ghost",
|
|
@@ -4964,10 +4809,10 @@ function ElementActions({ editor, selection }) {
|
|
|
4964
4809
|
"aria-label": prevLabel,
|
|
4965
4810
|
disabled: !selection.canMoveUp,
|
|
4966
4811
|
onClick: () => editor.move(-1, selection.id),
|
|
4967
|
-
children: /* @__PURE__ */
|
|
4812
|
+
children: /* @__PURE__ */ jsx10(Prev, {})
|
|
4968
4813
|
}
|
|
4969
4814
|
) }),
|
|
4970
|
-
/* @__PURE__ */
|
|
4815
|
+
/* @__PURE__ */ jsx10(TooltipWrap, { label: nextLabel, children: /* @__PURE__ */ jsx10(
|
|
4971
4816
|
Button,
|
|
4972
4817
|
{
|
|
4973
4818
|
variant: "ghost",
|
|
@@ -4975,10 +4820,10 @@ function ElementActions({ editor, selection }) {
|
|
|
4975
4820
|
"aria-label": nextLabel,
|
|
4976
4821
|
disabled: !selection.canMoveDown,
|
|
4977
4822
|
onClick: () => editor.move(1, selection.id),
|
|
4978
|
-
children: /* @__PURE__ */
|
|
4823
|
+
children: /* @__PURE__ */ jsx10(Next, {})
|
|
4979
4824
|
}
|
|
4980
4825
|
) }),
|
|
4981
|
-
/* @__PURE__ */
|
|
4826
|
+
/* @__PURE__ */ jsx10(TooltipWrap, { label: t("Duplicate"), children: /* @__PURE__ */ jsx10(
|
|
4982
4827
|
Button,
|
|
4983
4828
|
{
|
|
4984
4829
|
variant: "ghost",
|
|
@@ -4986,10 +4831,10 @@ function ElementActions({ editor, selection }) {
|
|
|
4986
4831
|
"aria-label": t("Duplicate"),
|
|
4987
4832
|
disabled: !selection.canDuplicate,
|
|
4988
4833
|
onClick: () => editor.duplicate(selection.id),
|
|
4989
|
-
children: /* @__PURE__ */
|
|
4834
|
+
children: /* @__PURE__ */ jsx10(Copy, {})
|
|
4990
4835
|
}
|
|
4991
4836
|
) }),
|
|
4992
|
-
/* @__PURE__ */
|
|
4837
|
+
/* @__PURE__ */ jsx10(TooltipWrap, { label: t("Delete (Del)"), children: /* @__PURE__ */ jsx10(
|
|
4993
4838
|
Button,
|
|
4994
4839
|
{
|
|
4995
4840
|
variant: "ghost",
|
|
@@ -4998,7 +4843,7 @@ function ElementActions({ editor, selection }) {
|
|
|
4998
4843
|
disabled: !selection.canRemove,
|
|
4999
4844
|
onClick: () => editor.remove(selection.id),
|
|
5000
4845
|
className: "eb:ml-auto eb:text-foreground-muted eb:hover:bg-destructive/10 eb:hover:text-destructive",
|
|
5001
|
-
children: /* @__PURE__ */
|
|
4846
|
+
children: /* @__PURE__ */ jsx10(Trash23, {})
|
|
5002
4847
|
}
|
|
5003
4848
|
) })
|
|
5004
4849
|
] });
|
|
@@ -5009,9 +4854,9 @@ import { useMemo as useMemo4, useRef as useRef6 } from "react";
|
|
|
5009
4854
|
import { X as X2 } from "lucide-react";
|
|
5010
4855
|
|
|
5011
4856
|
// src/ui/fields.jsx
|
|
5012
|
-
import { useEffect as
|
|
4857
|
+
import { useEffect as useEffect6, useRef as useRef4, useState as useState9 } from "react";
|
|
5013
4858
|
import { AlignCenter, AlignJustify, AlignLeft, AlignRight, Loader2 as Loader24, Upload as Upload2 } from "lucide-react";
|
|
5014
|
-
import { Fragment as Fragment5, jsx as
|
|
4859
|
+
import { Fragment as Fragment5, jsx as jsx11, jsxs as jsxs10 } from "react/jsx-runtime";
|
|
5015
4860
|
var COLOR_DEBOUNCE_MS = 250;
|
|
5016
4861
|
function emailColor(value) {
|
|
5017
4862
|
const v = String(value || "").trim();
|
|
@@ -5020,11 +4865,11 @@ function emailColor(value) {
|
|
|
5020
4865
|
return null;
|
|
5021
4866
|
}
|
|
5022
4867
|
function useDraft(value, toDraft) {
|
|
5023
|
-
const [draft, setDraft] =
|
|
5024
|
-
const ref =
|
|
5025
|
-
const convert =
|
|
4868
|
+
const [draft, setDraft] = useState9(() => toDraft(value));
|
|
4869
|
+
const ref = useRef4(null);
|
|
4870
|
+
const convert = useRef4(toDraft);
|
|
5026
4871
|
convert.current = toDraft;
|
|
5027
|
-
|
|
4872
|
+
useEffect6(() => {
|
|
5028
4873
|
const el = ref.current;
|
|
5029
4874
|
const focused = el && el.ownerDocument?.activeElement && el.contains(el.ownerDocument.activeElement);
|
|
5030
4875
|
if (!focused) setDraft(convert.current(value));
|
|
@@ -5033,10 +4878,10 @@ function useDraft(value, toDraft) {
|
|
|
5033
4878
|
}
|
|
5034
4879
|
var asText = (v) => v || "";
|
|
5035
4880
|
function FieldLabel({ id, children }) {
|
|
5036
|
-
return /* @__PURE__ */
|
|
4881
|
+
return /* @__PURE__ */ jsx11(Label, { htmlFor: id, className: "eb:text-[12px] eb:font-normal eb:text-foreground-lighter", children });
|
|
5037
4882
|
}
|
|
5038
4883
|
function FieldError({ children }) {
|
|
5039
|
-
return children ? /* @__PURE__ */
|
|
4884
|
+
return children ? /* @__PURE__ */ jsx11("p", { className: "eb:text-[12px] eb:text-destructive", children }) : null;
|
|
5040
4885
|
}
|
|
5041
4886
|
function TextField({ id, property, value, fallback = "", onCommit, addon = null, error = null }) {
|
|
5042
4887
|
const t = useT();
|
|
@@ -5067,23 +4912,23 @@ function TextField({ id, property, value, fallback = "", onCommit, addon = null,
|
|
|
5067
4912
|
onKeyDown: keyDown,
|
|
5068
4913
|
"aria-invalid": invalid || void 0
|
|
5069
4914
|
};
|
|
5070
|
-
return /* @__PURE__ */
|
|
5071
|
-
/* @__PURE__ */
|
|
5072
|
-
property.type === "textarea" ? /* @__PURE__ */
|
|
5073
|
-
/* @__PURE__ */
|
|
4915
|
+
return /* @__PURE__ */ jsxs10("div", { ref, className: "eb:space-y-1.5", children: [
|
|
4916
|
+
/* @__PURE__ */ jsx11(FieldLabel, { id, children: t(property.label) }),
|
|
4917
|
+
property.type === "textarea" ? /* @__PURE__ */ jsx11(Textarea, { rows: 3, ...shared }) : addon ? /* @__PURE__ */ jsxs10("div", { className: "eb:flex eb:gap-2", children: [
|
|
4918
|
+
/* @__PURE__ */ jsx11(Input, { ...shared, className: "eb:min-w-0 eb:flex-1" }),
|
|
5074
4919
|
addon
|
|
5075
|
-
] }) : /* @__PURE__ */
|
|
5076
|
-
/* @__PURE__ */
|
|
4920
|
+
] }) : /* @__PURE__ */ jsx11(Input, { ...shared }),
|
|
4921
|
+
/* @__PURE__ */ jsx11(FieldError, { children: invalid ? kind === "image" ? t("An https:// address is required") : t("Address not allowed") : error })
|
|
5077
4922
|
] });
|
|
5078
4923
|
}
|
|
5079
4924
|
function ImageField({ id, property, value, onCommit, services }) {
|
|
5080
4925
|
const t = useT();
|
|
5081
|
-
const inputRef =
|
|
4926
|
+
const inputRef = useRef4(
|
|
5082
4927
|
/** @type {HTMLInputElement|null} */
|
|
5083
4928
|
null
|
|
5084
4929
|
);
|
|
5085
|
-
const [busy, setBusy] =
|
|
5086
|
-
const [error, setError] =
|
|
4930
|
+
const [busy, setBusy] = useState9(false);
|
|
4931
|
+
const [error, setError] = useState9(
|
|
5087
4932
|
/** @type {string|null} */
|
|
5088
4933
|
null
|
|
5089
4934
|
);
|
|
@@ -5105,8 +4950,8 @@ function ImageField({ id, property, value, onCommit, services }) {
|
|
|
5105
4950
|
setBusy(false);
|
|
5106
4951
|
}
|
|
5107
4952
|
};
|
|
5108
|
-
const addon = upload ? /* @__PURE__ */
|
|
5109
|
-
/* @__PURE__ */
|
|
4953
|
+
const addon = upload ? /* @__PURE__ */ jsxs10(Fragment5, { children: [
|
|
4954
|
+
/* @__PURE__ */ jsx11(
|
|
5110
4955
|
"input",
|
|
5111
4956
|
{
|
|
5112
4957
|
ref: inputRef,
|
|
@@ -5119,7 +4964,7 @@ function ImageField({ id, property, value, onCommit, services }) {
|
|
|
5119
4964
|
onChange: pick
|
|
5120
4965
|
}
|
|
5121
4966
|
),
|
|
5122
|
-
/* @__PURE__ */
|
|
4967
|
+
/* @__PURE__ */ jsxs10(
|
|
5123
4968
|
Button,
|
|
5124
4969
|
{
|
|
5125
4970
|
type: "button",
|
|
@@ -5128,13 +4973,13 @@ function ImageField({ id, property, value, onCommit, services }) {
|
|
|
5128
4973
|
disabled: busy,
|
|
5129
4974
|
onClick: () => inputRef.current?.click(),
|
|
5130
4975
|
children: [
|
|
5131
|
-
busy ? /* @__PURE__ */
|
|
4976
|
+
busy ? /* @__PURE__ */ jsx11(Loader24, { className: "eb:animate-spin" }) : /* @__PURE__ */ jsx11(Upload2, {}),
|
|
5132
4977
|
t("Upload")
|
|
5133
4978
|
]
|
|
5134
4979
|
}
|
|
5135
4980
|
)
|
|
5136
4981
|
] }) : null;
|
|
5137
|
-
return /* @__PURE__ */
|
|
4982
|
+
return /* @__PURE__ */ jsx11(TextField, { id, property, value, onCommit, addon, error });
|
|
5138
4983
|
}
|
|
5139
4984
|
var asNumber = (v) => {
|
|
5140
4985
|
const n = Number.parseFloat(String(v || ""));
|
|
@@ -5156,10 +5001,10 @@ function SizeField({ id, property, value, fallback = "", onCommit }) {
|
|
|
5156
5001
|
const next = `${n}${property.unit || "px"}`;
|
|
5157
5002
|
if (next !== value) onCommit(next);
|
|
5158
5003
|
};
|
|
5159
|
-
return /* @__PURE__ */
|
|
5160
|
-
/* @__PURE__ */
|
|
5161
|
-
/* @__PURE__ */
|
|
5162
|
-
/* @__PURE__ */
|
|
5004
|
+
return /* @__PURE__ */ jsxs10("div", { ref, className: "eb:space-y-1.5", children: [
|
|
5005
|
+
/* @__PURE__ */ jsx11(FieldLabel, { id, children: t(property.label) }),
|
|
5006
|
+
/* @__PURE__ */ jsxs10("div", { className: "eb:relative", children: [
|
|
5007
|
+
/* @__PURE__ */ jsx11(
|
|
5163
5008
|
Input,
|
|
5164
5009
|
{
|
|
5165
5010
|
id,
|
|
@@ -5178,17 +5023,17 @@ function SizeField({ id, property, value, fallback = "", onCommit }) {
|
|
|
5178
5023
|
className: "eb:pr-9 eb:tabular-nums"
|
|
5179
5024
|
}
|
|
5180
5025
|
),
|
|
5181
|
-
/* @__PURE__ */
|
|
5026
|
+
/* @__PURE__ */ jsx11("span", { className: "eb:pointer-events-none eb:absolute eb:right-3 eb:top-1/2 eb:-translate-y-1/2 eb:text-[12px] eb:text-foreground-muted", children: property.unit || "px" })
|
|
5182
5027
|
] })
|
|
5183
5028
|
] });
|
|
5184
5029
|
}
|
|
5185
5030
|
function ColorField({ id, property, value, fallback = "", onCommit }) {
|
|
5186
5031
|
const t = useT();
|
|
5187
5032
|
const [draft, setDraft, ref] = useDraft(value, asText);
|
|
5188
|
-
const timer =
|
|
5189
|
-
const latest =
|
|
5033
|
+
const timer = useRef4(null);
|
|
5034
|
+
const latest = useRef4({ value, onCommit });
|
|
5190
5035
|
latest.current = { value, onCommit };
|
|
5191
|
-
|
|
5036
|
+
useEffect6(() => () => clearTimeout(timer.current), []);
|
|
5192
5037
|
const change = (next) => {
|
|
5193
5038
|
setDraft(next);
|
|
5194
5039
|
clearTimeout(timer.current);
|
|
@@ -5201,9 +5046,9 @@ function ColorField({ id, property, value, fallback = "", onCommit }) {
|
|
|
5201
5046
|
}
|
|
5202
5047
|
}, COLOR_DEBOUNCE_MS);
|
|
5203
5048
|
};
|
|
5204
|
-
return /* @__PURE__ */
|
|
5205
|
-
/* @__PURE__ */
|
|
5206
|
-
/* @__PURE__ */
|
|
5049
|
+
return /* @__PURE__ */ jsxs10("div", { ref, className: "eb:space-y-1.5", children: [
|
|
5050
|
+
/* @__PURE__ */ jsx11(FieldLabel, { id, children: t(property.label) }),
|
|
5051
|
+
/* @__PURE__ */ jsx11(ColorPicker, { id, value: draft, placeholder: emailColor(fallback) || "", onChange: change })
|
|
5207
5052
|
] });
|
|
5208
5053
|
}
|
|
5209
5054
|
var DEFAULT_OPTION = "__default__";
|
|
@@ -5213,9 +5058,9 @@ function SelectField({ id, property, value, fallback = "", onCommit }) {
|
|
|
5213
5058
|
const words = (v) => String(v || "").trim().split(/\s+/).sort().join(" ");
|
|
5214
5059
|
const fallbackOption = (property.options || []).find((o) => o.value && words(o.value) === words(fallback));
|
|
5215
5060
|
const defaultLabel = fallbackOption ? t("Default ({value})", { value: t(fallbackOption.label) }) : t("Default");
|
|
5216
|
-
return /* @__PURE__ */
|
|
5217
|
-
/* @__PURE__ */
|
|
5218
|
-
/* @__PURE__ */
|
|
5061
|
+
return /* @__PURE__ */ jsxs10("div", { className: "eb:space-y-1.5", children: [
|
|
5062
|
+
/* @__PURE__ */ jsx11(FieldLabel, { id, children: t(property.label) }),
|
|
5063
|
+
/* @__PURE__ */ jsxs10(
|
|
5219
5064
|
Select,
|
|
5220
5065
|
{
|
|
5221
5066
|
value: current,
|
|
@@ -5224,10 +5069,10 @@ function SelectField({ id, property, value, fallback = "", onCommit }) {
|
|
|
5224
5069
|
if (v !== (value || "")) onCommit(v || null);
|
|
5225
5070
|
},
|
|
5226
5071
|
children: [
|
|
5227
|
-
/* @__PURE__ */
|
|
5228
|
-
/* @__PURE__ */
|
|
5229
|
-
!(property.options || []).some((o) => !o.value) && /* @__PURE__ */
|
|
5230
|
-
(property.options || []).map((o) => /* @__PURE__ */
|
|
5072
|
+
/* @__PURE__ */ jsx11(SelectTrigger, { id, className: "eb:w-full", children: /* @__PURE__ */ jsx11(SelectValue, { placeholder: defaultLabel }) }),
|
|
5073
|
+
/* @__PURE__ */ jsxs10(SelectContent, { children: [
|
|
5074
|
+
!(property.options || []).some((o) => !o.value) && /* @__PURE__ */ jsx11(SelectItem, { value: DEFAULT_OPTION, children: defaultLabel }),
|
|
5075
|
+
(property.options || []).map((o) => /* @__PURE__ */ jsx11(SelectItem, { value: o.value || DEFAULT_OPTION, children: t(o.label) }, o.value || DEFAULT_OPTION))
|
|
5231
5076
|
] })
|
|
5232
5077
|
]
|
|
5233
5078
|
}
|
|
@@ -5237,9 +5082,9 @@ function SelectField({ id, property, value, fallback = "", onCommit }) {
|
|
|
5237
5082
|
var ALIGN_ICONS = { left: AlignLeft, center: AlignCenter, right: AlignRight, justify: AlignJustify };
|
|
5238
5083
|
function AlignField({ id, property, value, fallback = "", onCommit }) {
|
|
5239
5084
|
const t = useT();
|
|
5240
|
-
return /* @__PURE__ */
|
|
5241
|
-
/* @__PURE__ */
|
|
5242
|
-
/* @__PURE__ */
|
|
5085
|
+
return /* @__PURE__ */ jsxs10("div", { className: "eb:space-y-1.5", children: [
|
|
5086
|
+
/* @__PURE__ */ jsx11(FieldLabel, { id, children: t(property.label) }),
|
|
5087
|
+
/* @__PURE__ */ jsx11(
|
|
5243
5088
|
"div",
|
|
5244
5089
|
{
|
|
5245
5090
|
id,
|
|
@@ -5249,7 +5094,7 @@ function AlignField({ id, property, value, fallback = "", onCommit }) {
|
|
|
5249
5094
|
children: (property.options || []).map((option) => {
|
|
5250
5095
|
const Icon = ALIGN_ICONS[option.value] || AlignLeft;
|
|
5251
5096
|
const active = (value || fallback) === option.value;
|
|
5252
|
-
return /* @__PURE__ */
|
|
5097
|
+
return /* @__PURE__ */ jsx11(TooltipWrap, { label: t(option.label), children: /* @__PURE__ */ jsx11(
|
|
5253
5098
|
"button",
|
|
5254
5099
|
{
|
|
5255
5100
|
type: "button",
|
|
@@ -5261,7 +5106,7 @@ function AlignField({ id, property, value, fallback = "", onCommit }) {
|
|
|
5261
5106
|
"eb:inline-flex eb:h-7 eb:w-7 eb:items-center eb:justify-center eb:rounded-lg2 eb:transition-colors",
|
|
5262
5107
|
active ? "eb:bg-primary eb:text-primary-foreground" : "eb:text-foreground-lighter eb:hover:bg-surface-200 eb:hover:text-foreground"
|
|
5263
5108
|
),
|
|
5264
|
-
children: /* @__PURE__ */
|
|
5109
|
+
children: /* @__PURE__ */ jsx11(Icon, { className: "eb:h-3.5 eb:w-3.5" })
|
|
5265
5110
|
}
|
|
5266
5111
|
) }, option.value);
|
|
5267
5112
|
})
|
|
@@ -5290,10 +5135,10 @@ function SpacingField({ id, property, value, fallback = "", onCommit }) {
|
|
|
5290
5135
|
);
|
|
5291
5136
|
if (next !== (value || "")) onCommit(next);
|
|
5292
5137
|
};
|
|
5293
|
-
return /* @__PURE__ */
|
|
5294
|
-
/* @__PURE__ */
|
|
5295
|
-
/* @__PURE__ */
|
|
5296
|
-
/* @__PURE__ */
|
|
5138
|
+
return /* @__PURE__ */ jsxs10("fieldset", { ref, className: "eb:space-y-1.5", children: [
|
|
5139
|
+
/* @__PURE__ */ jsx11("legend", { className: "eb:mb-1.5 eb:text-[12px] eb:text-foreground-lighter", children: t(property.label) }),
|
|
5140
|
+
/* @__PURE__ */ jsx11("div", { className: "eb:grid eb:grid-cols-4 eb:gap-1.5", children: SIDE_LABELS.map(([side, label], i) => /* @__PURE__ */ jsxs10("div", { className: "eb:space-y-1", children: [
|
|
5141
|
+
/* @__PURE__ */ jsx11(
|
|
5297
5142
|
Input,
|
|
5298
5143
|
{
|
|
5299
5144
|
id: `${id}-${side}`,
|
|
@@ -5312,7 +5157,7 @@ function SpacingField({ id, property, value, fallback = "", onCommit }) {
|
|
|
5312
5157
|
className: "eb:px-2 eb:text-center eb:tabular-nums"
|
|
5313
5158
|
}
|
|
5314
5159
|
),
|
|
5315
|
-
/* @__PURE__ */
|
|
5160
|
+
/* @__PURE__ */ jsx11("span", { className: "eb:block eb:text-center eb:text-[10.5px] eb:text-foreground-muted", children: t(label) })
|
|
5316
5161
|
] }, side)) })
|
|
5317
5162
|
] });
|
|
5318
5163
|
}
|
|
@@ -5330,15 +5175,154 @@ var FIELDS = {
|
|
|
5330
5175
|
function PropertyField({ property, value, fallback = "", onCommit, idPrefix = "eb", services = null }) {
|
|
5331
5176
|
const Field = FIELDS[property.type] || TextField;
|
|
5332
5177
|
const id = `${idPrefix}-${property.key}`;
|
|
5333
|
-
return /* @__PURE__ */
|
|
5178
|
+
return /* @__PURE__ */ jsx11(Field, { id, property, value, fallback, onCommit, services });
|
|
5179
|
+
}
|
|
5180
|
+
|
|
5181
|
+
// src/features/variables/VariableInsertButton.jsx
|
|
5182
|
+
import { useState as useState11 } from "react";
|
|
5183
|
+
import { Braces as Braces3 } from "lucide-react";
|
|
5184
|
+
|
|
5185
|
+
// src/features/variables/VariablePicker.jsx
|
|
5186
|
+
import { useEffect as useEffect7, useMemo as useMemo3, useRef as useRef5, useState as useState10 } from "react";
|
|
5187
|
+
import { Pencil as Pencil2, Plus as Plus2, Search as Search2 } from "lucide-react";
|
|
5188
|
+
import { jsx as jsx12, jsxs as jsxs11 } from "react/jsx-runtime";
|
|
5189
|
+
function VariablePicker({ variables, onPick, onCreate = null, onEdit = null, filter }) {
|
|
5190
|
+
const t = useT();
|
|
5191
|
+
const [query, setQuery] = useState10("");
|
|
5192
|
+
const [active, setActive] = useState10(0);
|
|
5193
|
+
const listRef = useRef5(null);
|
|
5194
|
+
const searchRef = useRef5(
|
|
5195
|
+
/** @type {HTMLInputElement|null} */
|
|
5196
|
+
null
|
|
5197
|
+
);
|
|
5198
|
+
useSearchShortcut(searchRef);
|
|
5199
|
+
const matches = useMemo3(() => {
|
|
5200
|
+
const q = query.trim().toLowerCase();
|
|
5201
|
+
return variables.filter((v) => filter ? filter(v) : true).filter(
|
|
5202
|
+
(v) => !q || t(v.label).toLowerCase().includes(q) || v.key.toLowerCase().includes(q) || t(v.group).toLowerCase().includes(q)
|
|
5203
|
+
);
|
|
5204
|
+
}, [variables, query, filter, t]);
|
|
5205
|
+
const groups = useMemo3(() => [...new Set(matches.map((v) => v.group))], [matches]);
|
|
5206
|
+
const ordered = useMemo3(() => groups.flatMap((g) => matches.filter((v) => v.group === g)), [groups, matches]);
|
|
5207
|
+
useEffect7(() => setActive(0), [query]);
|
|
5208
|
+
useEffect7(() => {
|
|
5209
|
+
listRef.current?.querySelector('[data-active="true"]')?.scrollIntoView({ block: "nearest" });
|
|
5210
|
+
}, [active]);
|
|
5211
|
+
const onKeyDown = (e) => {
|
|
5212
|
+
if (e.key === "ArrowDown") {
|
|
5213
|
+
e.preventDefault();
|
|
5214
|
+
setActive((i) => Math.min(i + 1, ordered.length - 1));
|
|
5215
|
+
} else if (e.key === "ArrowUp") {
|
|
5216
|
+
e.preventDefault();
|
|
5217
|
+
setActive((i) => Math.max(i - 1, 0));
|
|
5218
|
+
} else if (e.key === "Enter") {
|
|
5219
|
+
e.preventDefault();
|
|
5220
|
+
if (ordered[active]) onPick(ordered[active]);
|
|
5221
|
+
else if (onCreate && query.trim()) onCreate(query.trim());
|
|
5222
|
+
}
|
|
5223
|
+
};
|
|
5224
|
+
return /* @__PURE__ */ jsxs11("div", { className: "eb:flex eb:max-h-[min(440px,70vh)] eb:flex-col", "data-testid": "variable-picker", children: [
|
|
5225
|
+
/* @__PURE__ */ jsxs11("div", { className: "eb:relative eb:border-b eb:border-border eb:p-2", children: [
|
|
5226
|
+
/* @__PURE__ */ jsx12(Search2, { className: "eb:pointer-events-none eb:absolute eb:top-1/2 eb:left-4 eb:size-3.5 eb:-translate-y-1/2 eb:text-foreground-muted" }),
|
|
5227
|
+
/* @__PURE__ */ jsx12(
|
|
5228
|
+
"input",
|
|
5229
|
+
{
|
|
5230
|
+
ref: searchRef,
|
|
5231
|
+
autoFocus: true,
|
|
5232
|
+
value: query,
|
|
5233
|
+
onChange: (e) => setQuery(e.target.value),
|
|
5234
|
+
onKeyDown,
|
|
5235
|
+
placeholder: t("Search variables\u2026"),
|
|
5236
|
+
"aria-label": t("Search variables\u2026"),
|
|
5237
|
+
"aria-keyshortcuts": SEARCH_KEYS,
|
|
5238
|
+
className: "eb:h-8 eb:w-full eb:rounded-md eb:bg-surface-75 eb:pr-14 eb:pl-7 eb:text-[13px] eb:text-foreground eb:outline-none eb:placeholder:text-foreground-muted eb:focus-visible:ring-2 eb:focus-visible:ring-ring/40"
|
|
5239
|
+
}
|
|
5240
|
+
),
|
|
5241
|
+
/* @__PURE__ */ jsx12(SearchKeyHint, { className: "eb:absolute eb:top-1/2 eb:right-4 eb:-translate-y-1/2" })
|
|
5242
|
+
] }),
|
|
5243
|
+
/* @__PURE__ */ jsxs11("div", { ref: listRef, role: "listbox", "aria-label": t("Variables"), className: "eb:min-h-0 eb:flex-1 eb:overflow-y-auto eb:p-1", children: [
|
|
5244
|
+
ordered.length === 0 && /* @__PURE__ */ jsxs11("div", { className: "eb:px-2 eb:py-3 eb:text-center eb:text-[12px] eb:text-foreground-muted", children: [
|
|
5245
|
+
/* @__PURE__ */ jsx12("p", { children: t("No variable found.") }),
|
|
5246
|
+
onCreate && query.trim() && /* @__PURE__ */ jsxs11(
|
|
5247
|
+
"button",
|
|
5248
|
+
{
|
|
5249
|
+
type: "button",
|
|
5250
|
+
onClick: () => onCreate(query.trim()),
|
|
5251
|
+
className: "eb:mt-2 eb:inline-flex eb:items-center eb:gap-1 eb:rounded-md eb:px-2 eb:py-1 eb:text-[12px] eb:font-medium eb:text-primary eb:hover:bg-accent",
|
|
5252
|
+
children: [
|
|
5253
|
+
/* @__PURE__ */ jsx12(Plus2, { className: "eb:size-3.5" }),
|
|
5254
|
+
t("Create \u201C{name}\u201D", { name: query.trim() })
|
|
5255
|
+
]
|
|
5256
|
+
}
|
|
5257
|
+
)
|
|
5258
|
+
] }),
|
|
5259
|
+
groups.map((group) => /* @__PURE__ */ jsxs11("div", { role: "group", "aria-label": t(group), children: [
|
|
5260
|
+
/* @__PURE__ */ jsx12("p", { className: "eb:px-2 eb:pt-2 eb:pb-1 eb:text-[11px] eb:text-foreground-muted", children: t(group) }),
|
|
5261
|
+
ordered.filter((v) => v.group === group).map((variable) => {
|
|
5262
|
+
const index = ordered.indexOf(variable);
|
|
5263
|
+
return /* @__PURE__ */ jsxs11(
|
|
5264
|
+
"div",
|
|
5265
|
+
{
|
|
5266
|
+
"data-active": index === active,
|
|
5267
|
+
className: cn(
|
|
5268
|
+
"eb:group/var eb:flex eb:items-center eb:gap-1 eb:rounded-md",
|
|
5269
|
+
index === active ? "eb:bg-accent eb:text-accent-foreground" : "eb:hover:bg-accent/60"
|
|
5270
|
+
),
|
|
5271
|
+
onMouseEnter: () => setActive(index),
|
|
5272
|
+
children: [
|
|
5273
|
+
/* @__PURE__ */ jsxs11(
|
|
5274
|
+
"button",
|
|
5275
|
+
{
|
|
5276
|
+
type: "button",
|
|
5277
|
+
role: "option",
|
|
5278
|
+
"aria-selected": index === active,
|
|
5279
|
+
"data-variable": variable.key,
|
|
5280
|
+
onClick: () => onPick(variable),
|
|
5281
|
+
className: "eb:flex eb:min-w-0 eb:flex-1 eb:items-center eb:gap-2 eb:px-2 eb:py-1.5 eb:text-left eb:text-[13px]",
|
|
5282
|
+
title: variable.description || void 0,
|
|
5283
|
+
children: [
|
|
5284
|
+
/* @__PURE__ */ jsx12("span", { className: "eb:min-w-0 eb:flex-1 eb:truncate", children: variable.custom ? variable.label : t(variable.label) }),
|
|
5285
|
+
/* @__PURE__ */ jsx12("code", { className: "eb:shrink-0 eb:font-mono eb:text-[10.5px] eb:text-foreground-muted", children: tokenOf(variable) })
|
|
5286
|
+
]
|
|
5287
|
+
}
|
|
5288
|
+
),
|
|
5289
|
+
onEdit && variable.custom && /* @__PURE__ */ jsx12(
|
|
5290
|
+
"button",
|
|
5291
|
+
{
|
|
5292
|
+
type: "button",
|
|
5293
|
+
"aria-label": t("Edit \u201C{name}\u201D", { name: variable.label }),
|
|
5294
|
+
title: t("Edit"),
|
|
5295
|
+
onClick: () => onEdit(variable),
|
|
5296
|
+
className: "eb:mr-1 eb:flex eb:size-6 eb:shrink-0 eb:items-center eb:justify-center eb:rounded-md eb:text-foreground-muted eb:opacity-0 eb:group-hover/var:opacity-100 eb:hover:bg-background eb:hover:text-foreground eb:focus-visible:opacity-100",
|
|
5297
|
+
children: /* @__PURE__ */ jsx12(Pencil2, { className: "eb:size-3" })
|
|
5298
|
+
}
|
|
5299
|
+
)
|
|
5300
|
+
]
|
|
5301
|
+
},
|
|
5302
|
+
variable.key
|
|
5303
|
+
);
|
|
5304
|
+
})
|
|
5305
|
+
] }, group))
|
|
5306
|
+
] }),
|
|
5307
|
+
onCreate && /* @__PURE__ */ jsx12("div", { className: "eb:border-t eb:border-border eb:p-1", children: /* @__PURE__ */ jsxs11(
|
|
5308
|
+
"button",
|
|
5309
|
+
{
|
|
5310
|
+
type: "button",
|
|
5311
|
+
onClick: () => onCreate(query.trim()),
|
|
5312
|
+
className: "eb:flex eb:w-full eb:items-center eb:gap-2 eb:rounded-md eb:px-2 eb:py-1.5 eb:text-left eb:text-[13px] eb:font-medium eb:text-primary eb:hover:bg-accent",
|
|
5313
|
+
children: [
|
|
5314
|
+
/* @__PURE__ */ jsx12(Plus2, { className: "eb:size-4" }),
|
|
5315
|
+
t("New variable")
|
|
5316
|
+
]
|
|
5317
|
+
}
|
|
5318
|
+
) })
|
|
5319
|
+
] });
|
|
5334
5320
|
}
|
|
5335
5321
|
|
|
5336
5322
|
// src/features/variables/VariableInsertButton.jsx
|
|
5337
|
-
import {
|
|
5338
|
-
import { Braces as Braces4 } from "lucide-react";
|
|
5339
|
-
import { jsx as jsx14, jsxs as jsxs13 } from "react/jsx-runtime";
|
|
5323
|
+
import { jsx as jsx13, jsxs as jsxs12 } from "react/jsx-runtime";
|
|
5340
5324
|
function VariableInsertButton({ variables, inputRef, value, onChange, label }) {
|
|
5341
|
-
const [open, setOpen] =
|
|
5325
|
+
const [open, setOpen] = useState11(false);
|
|
5342
5326
|
const insert = (variable) => {
|
|
5343
5327
|
const input = inputRef.current;
|
|
5344
5328
|
const text = String(value || "");
|
|
@@ -5354,14 +5338,14 @@ function VariableInsertButton({ variables, inputRef, value, onChange, label }) {
|
|
|
5354
5338
|
input.setSelectionRange(start + token.length, start + token.length);
|
|
5355
5339
|
});
|
|
5356
5340
|
};
|
|
5357
|
-
return /* @__PURE__ */
|
|
5358
|
-
/* @__PURE__ */
|
|
5359
|
-
/* @__PURE__ */
|
|
5341
|
+
return /* @__PURE__ */ jsxs12(Popover, { open, onOpenChange: setOpen, children: [
|
|
5342
|
+
/* @__PURE__ */ jsx13(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsx13(Button, { variant: "ghost", size: "icon-xs", "aria-label": label, title: label, children: /* @__PURE__ */ jsx13(Braces3, {}) }) }),
|
|
5343
|
+
/* @__PURE__ */ jsx13(PopoverContent, { align: "end", className: "eb:w-72 eb:p-0", onCloseAutoFocus: (e) => e.preventDefault(), children: /* @__PURE__ */ jsx13(VariablePicker, { variables, onPick: insert, filter: (v) => v.kind !== "link" }) })
|
|
5360
5344
|
] });
|
|
5361
5345
|
}
|
|
5362
5346
|
|
|
5363
5347
|
// src/ui/PropertiesPanel.jsx
|
|
5364
|
-
import { Fragment as Fragment6, jsx as
|
|
5348
|
+
import { Fragment as Fragment6, jsx as jsx14, jsxs as jsxs13 } from "react/jsx-runtime";
|
|
5365
5349
|
var GROUP_ORDER = Object.values(GROUPS);
|
|
5366
5350
|
function grouped(properties) {
|
|
5367
5351
|
const map = /* @__PURE__ */ new Map();
|
|
@@ -5375,9 +5359,9 @@ function grouped(properties) {
|
|
|
5375
5359
|
}
|
|
5376
5360
|
function PropertyGroups({ editor, element, block, idPrefix, services = null }) {
|
|
5377
5361
|
const t = useT();
|
|
5378
|
-
return grouped(block?.properties || []).map(([group, properties]) => /* @__PURE__ */
|
|
5379
|
-
/* @__PURE__ */
|
|
5380
|
-
properties.map((property) => /* @__PURE__ */
|
|
5362
|
+
return grouped(block?.properties || []).map(([group, properties]) => /* @__PURE__ */ jsxs13("section", { className: "eb:space-y-3", children: [
|
|
5363
|
+
/* @__PURE__ */ jsx14(SectionLabel, { children: t(group) }),
|
|
5364
|
+
properties.map((property) => /* @__PURE__ */ jsx14(
|
|
5381
5365
|
PropertyField,
|
|
5382
5366
|
{
|
|
5383
5367
|
idPrefix,
|
|
@@ -5395,9 +5379,9 @@ var LABEL_PROPERTY = { key: "content", label: "Label", type: "text", group: GROU
|
|
|
5395
5379
|
var REWRITABLE_TAGS = /* @__PURE__ */ new Set(["mj-text", "mj-button"]);
|
|
5396
5380
|
function PanelHeader({ title, onClose, closeLabel = "Deselect" }) {
|
|
5397
5381
|
const t = useT();
|
|
5398
|
-
return /* @__PURE__ */
|
|
5399
|
-
/* @__PURE__ */
|
|
5400
|
-
onClose && /* @__PURE__ */
|
|
5382
|
+
return /* @__PURE__ */ jsxs13("header", { className: "eb:sticky eb:top-0 eb:z-10 eb:flex eb:h-11 eb:shrink-0 eb:items-center eb:gap-2 eb:border-b eb:border-border eb:bg-background eb:px-4", children: [
|
|
5383
|
+
/* @__PURE__ */ jsx14("h3", { className: "eb:min-w-0 eb:flex-1 eb:text-[13px] eb:font-medium eb:text-foreground", children: /* @__PURE__ */ jsx14(TruncatedText, { children: title }) }),
|
|
5384
|
+
onClose && /* @__PURE__ */ jsx14(Button, { variant: "ghost", size: "icon-sm-compact", onClick: onClose, "aria-label": t(closeLabel), children: /* @__PURE__ */ jsx14(X2, {}) })
|
|
5401
5385
|
] });
|
|
5402
5386
|
}
|
|
5403
5387
|
function TemplateSettings({ editor, meta, onMetaChange, changes, showErrors, variables = null, ai = null, onDismiss = null }) {
|
|
@@ -5408,18 +5392,18 @@ function TemplateSettings({ editor, meta, onMetaChange, changes, showErrors, var
|
|
|
5408
5392
|
const root = useMemo4(() => rootId ? editor.getElement(rootId) : null, [editor, rootId, changes]);
|
|
5409
5393
|
const bodyBlock2 = root?.blockId ? editor.getBlock(root.blockId) : null;
|
|
5410
5394
|
const field = (key) => (e) => onMetaChange({ ...meta, [key]: e.target.value });
|
|
5411
|
-
return /* @__PURE__ */
|
|
5412
|
-
/* @__PURE__ */
|
|
5413
|
-
/* @__PURE__ */
|
|
5414
|
-
/* @__PURE__ */
|
|
5415
|
-
/* @__PURE__ */
|
|
5416
|
-
/* @__PURE__ */
|
|
5417
|
-
/* @__PURE__ */
|
|
5395
|
+
return /* @__PURE__ */ jsxs13(Fragment6, { children: [
|
|
5396
|
+
/* @__PURE__ */ jsx14(PanelHeader, { title: t("Email"), onClose: onDismiss, closeLabel: "Close" }),
|
|
5397
|
+
/* @__PURE__ */ jsxs13("div", { className: "eb:space-y-5 eb:p-4", children: [
|
|
5398
|
+
/* @__PURE__ */ jsxs13("section", { className: "eb:space-y-3", children: [
|
|
5399
|
+
/* @__PURE__ */ jsx14(SectionLabel, { children: t("Template") }),
|
|
5400
|
+
/* @__PURE__ */ jsxs13("div", { className: "eb:space-y-1.5", children: [
|
|
5401
|
+
/* @__PURE__ */ jsxs13(Label, { htmlFor: "eb-meta-name", className: "eb:text-[12px] eb:font-normal eb:text-foreground-lighter", children: [
|
|
5418
5402
|
t("Name"),
|
|
5419
5403
|
" ",
|
|
5420
|
-
/* @__PURE__ */
|
|
5404
|
+
/* @__PURE__ */ jsx14("span", { className: "eb:text-destructive", children: "*" })
|
|
5421
5405
|
] }),
|
|
5422
|
-
/* @__PURE__ */
|
|
5406
|
+
/* @__PURE__ */ jsx14(
|
|
5423
5407
|
Input,
|
|
5424
5408
|
{
|
|
5425
5409
|
id: "eb-meta-name",
|
|
@@ -5429,12 +5413,12 @@ function TemplateSettings({ editor, meta, onMetaChange, changes, showErrors, var
|
|
|
5429
5413
|
"aria-invalid": showErrors && !meta.name.trim() || void 0
|
|
5430
5414
|
}
|
|
5431
5415
|
),
|
|
5432
|
-
showErrors && !meta.name.trim() && /* @__PURE__ */
|
|
5416
|
+
showErrors && !meta.name.trim() && /* @__PURE__ */ jsx14("p", { className: "eb:text-[12px] eb:text-destructive", children: t("Name is required") })
|
|
5433
5417
|
] }),
|
|
5434
|
-
/* @__PURE__ */
|
|
5435
|
-
/* @__PURE__ */
|
|
5436
|
-
/* @__PURE__ */
|
|
5437
|
-
variables && /* @__PURE__ */
|
|
5418
|
+
/* @__PURE__ */ jsxs13("div", { className: "eb:space-y-1.5", children: [
|
|
5419
|
+
/* @__PURE__ */ jsxs13("div", { className: "eb:flex eb:h-6 eb:items-center eb:justify-between", children: [
|
|
5420
|
+
/* @__PURE__ */ jsx14(Label, { htmlFor: "eb-meta-subject", className: "eb:text-[12px] eb:font-normal eb:text-foreground-lighter", children: t("Subject") }),
|
|
5421
|
+
variables && /* @__PURE__ */ jsx14(
|
|
5438
5422
|
VariableInsertButton,
|
|
5439
5423
|
{
|
|
5440
5424
|
variables,
|
|
@@ -5445,8 +5429,8 @@ function TemplateSettings({ editor, meta, onMetaChange, changes, showErrors, var
|
|
|
5445
5429
|
}
|
|
5446
5430
|
)
|
|
5447
5431
|
] }),
|
|
5448
|
-
/* @__PURE__ */
|
|
5449
|
-
ai?.suggestSubjects && /* @__PURE__ */
|
|
5432
|
+
/* @__PURE__ */ jsx14(Input, { ref: subjectRef, id: "eb-meta-subject", maxLength: 250, value: meta.subject, onChange: field("subject") }),
|
|
5433
|
+
ai?.suggestSubjects && /* @__PURE__ */ jsx14(
|
|
5450
5434
|
ai.components.Subjects,
|
|
5451
5435
|
{
|
|
5452
5436
|
editor,
|
|
@@ -5457,10 +5441,10 @@ function TemplateSettings({ editor, meta, onMetaChange, changes, showErrors, var
|
|
|
5457
5441
|
}
|
|
5458
5442
|
)
|
|
5459
5443
|
] }),
|
|
5460
|
-
/* @__PURE__ */
|
|
5461
|
-
/* @__PURE__ */
|
|
5462
|
-
/* @__PURE__ */
|
|
5463
|
-
variables && /* @__PURE__ */
|
|
5444
|
+
/* @__PURE__ */ jsxs13("div", { className: "eb:space-y-1.5", children: [
|
|
5445
|
+
/* @__PURE__ */ jsxs13("div", { className: "eb:flex eb:h-6 eb:items-center eb:justify-between", children: [
|
|
5446
|
+
/* @__PURE__ */ jsx14(Label, { htmlFor: "eb-meta-preheader", className: "eb:text-[12px] eb:font-normal eb:text-foreground-lighter", children: t("Preview text") }),
|
|
5447
|
+
variables && /* @__PURE__ */ jsx14(
|
|
5464
5448
|
VariableInsertButton,
|
|
5465
5449
|
{
|
|
5466
5450
|
variables,
|
|
@@ -5471,7 +5455,7 @@ function TemplateSettings({ editor, meta, onMetaChange, changes, showErrors, var
|
|
|
5471
5455
|
}
|
|
5472
5456
|
)
|
|
5473
5457
|
] }),
|
|
5474
|
-
/* @__PURE__ */
|
|
5458
|
+
/* @__PURE__ */ jsx14(
|
|
5475
5459
|
Textarea,
|
|
5476
5460
|
{
|
|
5477
5461
|
ref: preheaderRef,
|
|
@@ -5485,7 +5469,7 @@ function TemplateSettings({ editor, meta, onMetaChange, changes, showErrors, var
|
|
|
5485
5469
|
)
|
|
5486
5470
|
] })
|
|
5487
5471
|
] }),
|
|
5488
|
-
root && bodyBlock2 && /* @__PURE__ */
|
|
5472
|
+
root && bodyBlock2 && /* @__PURE__ */ jsx14(PropertyGroups, { editor, element: root, block: bodyBlock2, idPrefix: "eb-page" })
|
|
5489
5473
|
] })
|
|
5490
5474
|
] });
|
|
5491
5475
|
}
|
|
@@ -5504,7 +5488,7 @@ function PropertiesPanel({
|
|
|
5504
5488
|
}) {
|
|
5505
5489
|
const t = useT();
|
|
5506
5490
|
const block = selection?.blockId ? editor.getBlock(selection.blockId) : null;
|
|
5507
|
-
return /* @__PURE__ */
|
|
5491
|
+
return /* @__PURE__ */ jsx14("aside", { "aria-label": t("Properties"), className: "eb:min-h-0 eb:overflow-y-auto eb:border-l eb:border-border", "data-testid": "eb-properties", children: !selection ? /* @__PURE__ */ jsx14(
|
|
5508
5492
|
TemplateSettings,
|
|
5509
5493
|
{
|
|
5510
5494
|
editor,
|
|
@@ -5516,8 +5500,8 @@ function PropertiesPanel({
|
|
|
5516
5500
|
ai,
|
|
5517
5501
|
onDismiss
|
|
5518
5502
|
}
|
|
5519
|
-
) : /* @__PURE__ */
|
|
5520
|
-
/* @__PURE__ */
|
|
5503
|
+
) : /* @__PURE__ */ jsxs13(Fragment6, { children: [
|
|
5504
|
+
/* @__PURE__ */ jsx14(
|
|
5521
5505
|
PanelHeader,
|
|
5522
5506
|
{
|
|
5523
5507
|
title: t(selection.label),
|
|
@@ -5526,12 +5510,12 @@ function PropertiesPanel({
|
|
|
5526
5510
|
}
|
|
5527
5511
|
),
|
|
5528
5512
|
actions,
|
|
5529
|
-
selection.multiple ? /* @__PURE__ */
|
|
5513
|
+
selection.multiple ? /* @__PURE__ */ jsx14("p", { className: "eb:p-4 eb:text-[13px] eb:leading-relaxed eb:text-foreground-lighter", "data-testid": "eb-select-all", children: t("{count} sections selected. Press Delete to remove them (Ctrl+Z brings them back).", {
|
|
5530
5514
|
count: selection.multiple
|
|
5531
|
-
}) }) : /* @__PURE__ */
|
|
5532
|
-
selection.content !== null && selection.content !== void 0 && /* @__PURE__ */
|
|
5533
|
-
/* @__PURE__ */
|
|
5534
|
-
/* @__PURE__ */
|
|
5515
|
+
}) }) : /* @__PURE__ */ jsxs13("div", { className: "eb:space-y-5 eb:p-4", children: [
|
|
5516
|
+
selection.content !== null && selection.content !== void 0 && /* @__PURE__ */ jsxs13("section", { className: "eb:space-y-3", children: [
|
|
5517
|
+
/* @__PURE__ */ jsx14(SectionLabel, { children: t(GROUPS.content) }),
|
|
5518
|
+
/* @__PURE__ */ jsx14(
|
|
5535
5519
|
PropertyField,
|
|
5536
5520
|
{
|
|
5537
5521
|
idPrefix: "eb-prop",
|
|
@@ -5542,16 +5526,16 @@ function PropertiesPanel({
|
|
|
5542
5526
|
`${selection.id}:content`
|
|
5543
5527
|
)
|
|
5544
5528
|
] }),
|
|
5545
|
-
selection.tagName === "mj-text" && /* @__PURE__ */
|
|
5546
|
-
ai?.rewrite && REWRITABLE_TAGS.has(selection.tagName) && /* @__PURE__ */
|
|
5547
|
-
/* @__PURE__ */
|
|
5529
|
+
selection.tagName === "mj-text" && /* @__PURE__ */ jsx14("p", { className: "eb:text-[12px] eb:text-foreground-muted", children: t("Double-click the text to edit it.") }),
|
|
5530
|
+
ai?.rewrite && REWRITABLE_TAGS.has(selection.tagName) && /* @__PURE__ */ jsx14(ai.components.Rewrite, { editor, selection, rewrite: ai.rewrite }, selection.id),
|
|
5531
|
+
/* @__PURE__ */ jsx14(PropertyGroups, { editor, element: selection, block, idPrefix: "eb-prop", services })
|
|
5548
5532
|
] })
|
|
5549
5533
|
] }) });
|
|
5550
5534
|
}
|
|
5551
5535
|
|
|
5552
5536
|
// src/ui/Toolbar.jsx
|
|
5553
5537
|
import { ArrowLeft as ArrowLeft2, Monitor, Redo2, Smartphone, Undo2 } from "lucide-react";
|
|
5554
|
-
import { Fragment as Fragment7, jsx as
|
|
5538
|
+
import { Fragment as Fragment7, jsx as jsx15, jsxs as jsxs14 } from "react/jsx-runtime";
|
|
5555
5539
|
function Toolbar({
|
|
5556
5540
|
title,
|
|
5557
5541
|
status,
|
|
@@ -5568,8 +5552,8 @@ function Toolbar({
|
|
|
5568
5552
|
compact = false
|
|
5569
5553
|
}) {
|
|
5570
5554
|
const t = useT();
|
|
5571
|
-
const identity2 = /* @__PURE__ */
|
|
5572
|
-
/* @__PURE__ */
|
|
5555
|
+
const identity2 = /* @__PURE__ */ jsxs14(Fragment7, { children: [
|
|
5556
|
+
/* @__PURE__ */ jsxs14(
|
|
5573
5557
|
Button,
|
|
5574
5558
|
{
|
|
5575
5559
|
variant: "ghost",
|
|
@@ -5577,50 +5561,50 @@ function Toolbar({
|
|
|
5577
5561
|
onClick: onBack,
|
|
5578
5562
|
"aria-label": compact ? t("Back") : void 0,
|
|
5579
5563
|
children: [
|
|
5580
|
-
/* @__PURE__ */
|
|
5564
|
+
/* @__PURE__ */ jsx15(ArrowLeft2, {}),
|
|
5581
5565
|
!compact && t("Back")
|
|
5582
5566
|
]
|
|
5583
5567
|
}
|
|
5584
5568
|
),
|
|
5585
|
-
/* @__PURE__ */
|
|
5586
|
-
/* @__PURE__ */
|
|
5587
|
-
/* @__PURE__ */
|
|
5588
|
-
/* @__PURE__ */
|
|
5569
|
+
/* @__PURE__ */ jsx15(Separator, { orientation: "vertical", className: "eb:h-5" }),
|
|
5570
|
+
/* @__PURE__ */ jsxs14("div", { className: cn("eb:min-w-0 eb:flex-1", !compact && "eb:max-w-[320px]"), children: [
|
|
5571
|
+
/* @__PURE__ */ jsx15("p", { className: "eb:text-[13px] eb:font-medium eb:text-foreground", children: /* @__PURE__ */ jsx15(TruncatedText, { children: title || t("New template") }) }),
|
|
5572
|
+
/* @__PURE__ */ jsx15("p", { className: "eb:text-[11px] eb:text-foreground-muted", role: "status", "aria-live": "polite", children: status })
|
|
5589
5573
|
] })
|
|
5590
5574
|
] });
|
|
5591
|
-
const editing = /* @__PURE__ */
|
|
5592
|
-
/* @__PURE__ */
|
|
5593
|
-
/* @__PURE__ */
|
|
5594
|
-
/* @__PURE__ */
|
|
5575
|
+
const editing = /* @__PURE__ */ jsxs14("div", { className: "eb:flex eb:shrink-0 eb:items-center eb:gap-2", children: [
|
|
5576
|
+
/* @__PURE__ */ jsxs14("div", { className: "eb:flex eb:items-center eb:gap-0.5", children: [
|
|
5577
|
+
/* @__PURE__ */ jsx15(TooltipWrap, { label: t("Undo (Ctrl+Z)"), children: /* @__PURE__ */ jsx15(Button, { variant: "ghost", size: "icon-sm-compact", onClick: onUndo, disabled: !ready || !canUndo, "aria-label": t("Undo"), children: /* @__PURE__ */ jsx15(Undo2, {}) }) }),
|
|
5578
|
+
/* @__PURE__ */ jsx15(TooltipWrap, { label: t("Redo (Ctrl+Y)"), children: /* @__PURE__ */ jsx15(Button, { variant: "ghost", size: "icon-sm-compact", onClick: onRedo, disabled: !ready || !canRedo, "aria-label": t("Redo"), children: /* @__PURE__ */ jsx15(Redo2, {}) }) })
|
|
5595
5579
|
] }),
|
|
5596
|
-
/* @__PURE__ */
|
|
5597
|
-
/* @__PURE__ */
|
|
5598
|
-
/* @__PURE__ */
|
|
5580
|
+
/* @__PURE__ */ jsx15(Tabs, { value: device, onValueChange: onDeviceChange, children: /* @__PURE__ */ jsxs14(TabsList, { "aria-label": t("Device"), children: [
|
|
5581
|
+
/* @__PURE__ */ jsxs14(TabsTrigger, { value: "desktop", disabled: !ready, "aria-label": compact ? t("Desktop") : void 0, children: [
|
|
5582
|
+
/* @__PURE__ */ jsx15(Monitor, {}),
|
|
5599
5583
|
!compact && t("Desktop")
|
|
5600
5584
|
] }),
|
|
5601
|
-
/* @__PURE__ */
|
|
5602
|
-
/* @__PURE__ */
|
|
5585
|
+
/* @__PURE__ */ jsxs14(TabsTrigger, { value: "mobile", disabled: !ready, "aria-label": compact ? t("Mobile") : void 0, children: [
|
|
5586
|
+
/* @__PURE__ */ jsx15(Smartphone, {}),
|
|
5603
5587
|
!compact && t("Mobile")
|
|
5604
5588
|
] })
|
|
5605
5589
|
] }) })
|
|
5606
5590
|
] });
|
|
5607
5591
|
if (compact) {
|
|
5608
|
-
return /* @__PURE__ */
|
|
5609
|
-
/* @__PURE__ */
|
|
5592
|
+
return /* @__PURE__ */ jsxs14("div", { className: "eb:shrink-0 eb:border-b eb:border-border", "data-layout": "compact", children: [
|
|
5593
|
+
/* @__PURE__ */ jsxs14("div", { className: "eb:flex eb:h-12 eb:items-center eb:gap-2 eb:px-2", children: [
|
|
5610
5594
|
identity2,
|
|
5611
5595
|
primary
|
|
5612
5596
|
] }),
|
|
5613
|
-
/* @__PURE__ */
|
|
5597
|
+
/* @__PURE__ */ jsxs14("div", { className: "eb:flex eb:items-center eb:gap-2 eb:overflow-x-auto eb:px-2 eb:pb-2", "data-testid": "eb-toolbar-actions", children: [
|
|
5614
5598
|
editing,
|
|
5615
|
-
/* @__PURE__ */
|
|
5616
|
-
/* @__PURE__ */
|
|
5599
|
+
/* @__PURE__ */ jsx15(Separator, { orientation: "vertical", className: "eb:h-5 eb:shrink-0" }),
|
|
5600
|
+
/* @__PURE__ */ jsx15("div", { className: "eb:flex eb:shrink-0 eb:items-center eb:gap-2", children: actions })
|
|
5617
5601
|
] })
|
|
5618
5602
|
] });
|
|
5619
5603
|
}
|
|
5620
|
-
return /* @__PURE__ */
|
|
5604
|
+
return /* @__PURE__ */ jsxs14("div", { className: "eb:flex eb:h-12 eb:shrink-0 eb:items-center eb:gap-3 eb:border-b eb:border-border eb:px-3", children: [
|
|
5621
5605
|
identity2,
|
|
5622
5606
|
editing,
|
|
5623
|
-
/* @__PURE__ */
|
|
5607
|
+
/* @__PURE__ */ jsxs14("div", { className: "eb:ml-auto eb:flex eb:items-center eb:gap-2", "data-testid": "eb-toolbar-actions", children: [
|
|
5624
5608
|
actions,
|
|
5625
5609
|
primary
|
|
5626
5610
|
] })
|
|
@@ -5630,7 +5614,7 @@ function Toolbar({
|
|
|
5630
5614
|
// src/ui/Drawer.jsx
|
|
5631
5615
|
import { useEffect as useEffect8 } from "react";
|
|
5632
5616
|
import { X as X3 } from "lucide-react";
|
|
5633
|
-
import { jsx as
|
|
5617
|
+
import { jsx as jsx16, jsxs as jsxs15 } from "react/jsx-runtime";
|
|
5634
5618
|
function Drawer({ open, side = "left", title, onClose, children, testId, header: header2 = true }) {
|
|
5635
5619
|
const t = useT();
|
|
5636
5620
|
useEffect8(() => {
|
|
@@ -5641,9 +5625,9 @@ function Drawer({ open, side = "left", title, onClose, children, testId, header:
|
|
|
5641
5625
|
window.addEventListener("keydown", onKey);
|
|
5642
5626
|
return () => window.removeEventListener("keydown", onKey);
|
|
5643
5627
|
}, [open, onClose]);
|
|
5644
|
-
return /* @__PURE__ */
|
|
5645
|
-
/* @__PURE__ */
|
|
5646
|
-
/* @__PURE__ */
|
|
5628
|
+
return /* @__PURE__ */ jsxs15("div", { className: cn("eb:absolute eb:inset-0 eb:z-20", !open && "eb:hidden"), "data-testid": testId, "data-open": open ? "true" : "false", children: [
|
|
5629
|
+
/* @__PURE__ */ jsx16("div", { className: "eb:absolute eb:inset-0 eb:bg-black/30", onClick: onClose, "aria-hidden": "true" }),
|
|
5630
|
+
/* @__PURE__ */ jsxs15(
|
|
5647
5631
|
"section",
|
|
5648
5632
|
{
|
|
5649
5633
|
role: "dialog",
|
|
@@ -5653,11 +5637,11 @@ function Drawer({ open, side = "left", title, onClose, children, testId, header:
|
|
|
5653
5637
|
side === "left" ? "eb:left-0 eb:border-r eb:border-border" : "eb:right-0 eb:border-l eb:border-border"
|
|
5654
5638
|
),
|
|
5655
5639
|
children: [
|
|
5656
|
-
header2 && /* @__PURE__ */
|
|
5657
|
-
/* @__PURE__ */
|
|
5658
|
-
/* @__PURE__ */
|
|
5640
|
+
header2 && /* @__PURE__ */ jsxs15("header", { className: "eb:flex eb:h-10 eb:shrink-0 eb:items-center eb:justify-between eb:border-b eb:border-border eb:px-3", children: [
|
|
5641
|
+
/* @__PURE__ */ jsx16("h3", { className: "eb:text-[13px] eb:font-medium eb:text-foreground", children: title }),
|
|
5642
|
+
/* @__PURE__ */ jsx16(Button, { variant: "ghost", size: "icon-sm-compact", onClick: onClose, "aria-label": t("Close"), children: /* @__PURE__ */ jsx16(X3, {}) })
|
|
5659
5643
|
] }),
|
|
5660
|
-
/* @__PURE__ */
|
|
5644
|
+
/* @__PURE__ */ jsx16("div", { className: "eb:grid eb:min-h-0 eb:flex-1", children })
|
|
5661
5645
|
]
|
|
5662
5646
|
}
|
|
5663
5647
|
)
|
|
@@ -5665,10 +5649,10 @@ function Drawer({ open, side = "left", title, onClose, children, testId, header:
|
|
|
5665
5649
|
}
|
|
5666
5650
|
|
|
5667
5651
|
// src/ui/useEmailEditorState.js
|
|
5668
|
-
import { useCallback as useCallback2, useEffect as useEffect9, useRef as useRef7, useState as
|
|
5652
|
+
import { useCallback as useCallback2, useEffect as useEffect9, useRef as useRef7, useState as useState12 } from "react";
|
|
5669
5653
|
var DIRTY_CHECK_MS = 150;
|
|
5670
5654
|
function useEmailEditorState(editor) {
|
|
5671
|
-
const [state, setState] =
|
|
5655
|
+
const [state, setState] = useState12(() => ({
|
|
5672
5656
|
status: editor?.status || "idle",
|
|
5673
5657
|
selection: null,
|
|
5674
5658
|
canUndo: false,
|
|
@@ -5749,7 +5733,7 @@ function useEmailEditorState(editor) {
|
|
|
5749
5733
|
}
|
|
5750
5734
|
|
|
5751
5735
|
// src/ui/EmailBuilder.jsx
|
|
5752
|
-
import { Fragment as Fragment8, jsx as
|
|
5736
|
+
import { Fragment as Fragment8, jsx as jsx17, jsxs as jsxs16 } from "react/jsx-runtime";
|
|
5753
5737
|
var EMPTY_META = { name: "", subject: "", preheader: "", description: "" };
|
|
5754
5738
|
function metaOf(template) {
|
|
5755
5739
|
return {
|
|
@@ -5787,7 +5771,7 @@ function Builder({
|
|
|
5787
5771
|
);
|
|
5788
5772
|
const storage = useMemo5(() => injectedStorage || createHttpStorage(), [injectedStorage]);
|
|
5789
5773
|
const systemVariables = useMemo5(() => defineVariables(variablesProp || DEFAULT_VARIABLES), [variablesProp]);
|
|
5790
|
-
const [customVariables, setCustomVariables] =
|
|
5774
|
+
const [customVariables, setCustomVariables] = useState13([]);
|
|
5791
5775
|
const variables = useMemo5(() => mergeVariables(systemVariables, customVariables), [systemVariables, customVariables]);
|
|
5792
5776
|
const canManageVariables = typeof storage.saveVariable === "function" && typeof storage.listVariables === "function";
|
|
5793
5777
|
const reloadVariables = useCallback3(async () => {
|
|
@@ -5821,20 +5805,20 @@ function Builder({
|
|
|
5821
5805
|
const state = useEmailEditorState(editor);
|
|
5822
5806
|
const autosaveOn = isFeatureEnabled("autosave");
|
|
5823
5807
|
const autosaveOptions = emailBuilderFeatures.get("autosave")?.options || {};
|
|
5824
|
-
const [template, setTemplate] =
|
|
5825
|
-
const [boot, setBoot] =
|
|
5826
|
-
const [meta, setMeta] =
|
|
5827
|
-
const [savedMeta, setSavedMeta] =
|
|
5828
|
-
const [saving, setSaving] =
|
|
5829
|
-
const [showErrors, setShowErrors] =
|
|
5830
|
-
const [autosaveBlocked, setAutosaveBlocked] =
|
|
5831
|
-
const [engineError, setEngineError] =
|
|
5832
|
-
const [attempt, setAttempt] =
|
|
5808
|
+
const [template, setTemplate] = useState13({ id: initialTemplateId, revision: null, savedAt: null, auto: false });
|
|
5809
|
+
const [boot, setBoot] = useState13({ status: "loading", document: null, mjml: null, draft: false, message: null });
|
|
5810
|
+
const [meta, setMeta] = useState13(EMPTY_META);
|
|
5811
|
+
const [savedMeta, setSavedMeta] = useState13(EMPTY_META);
|
|
5812
|
+
const [saving, setSaving] = useState13(null);
|
|
5813
|
+
const [showErrors, setShowErrors] = useState13(false);
|
|
5814
|
+
const [autosaveBlocked, setAutosaveBlocked] = useState13(null);
|
|
5815
|
+
const [engineError, setEngineError] = useState13(null);
|
|
5816
|
+
const [attempt, setAttempt] = useState13(0);
|
|
5833
5817
|
const rootRef = useRef8(null);
|
|
5834
5818
|
const layout2 = useLayoutWidth(rootRef);
|
|
5835
5819
|
const compact = layout2 !== "wide";
|
|
5836
5820
|
const blocksInDrawer = layout2 === "narrow";
|
|
5837
|
-
const [drawer, setDrawer] =
|
|
5821
|
+
const [drawer, setDrawer] = useState13(null);
|
|
5838
5822
|
const closeDrawer = useCallback3(() => setDrawer(null), []);
|
|
5839
5823
|
useEffect10(() => {
|
|
5840
5824
|
if (!compact) setDrawer(null);
|
|
@@ -6103,7 +6087,7 @@ function Builder({
|
|
|
6103
6087
|
const featuresWith = (slot) => emailBuilderFeatures.list((f) => f.enabled !== false && Boolean(f[slot]) && (f.isAvailable ? f.isAvailable(featureContext) : true));
|
|
6104
6088
|
const toolbarFeatures = featuresWith("toolbar");
|
|
6105
6089
|
const canvasBars = featuresWith("canvasBar");
|
|
6106
|
-
const properties = /* @__PURE__ */
|
|
6090
|
+
const properties = /* @__PURE__ */ jsx17(
|
|
6107
6091
|
PropertiesPanel,
|
|
6108
6092
|
{
|
|
6109
6093
|
onDismiss: compact ? closeDrawer : null,
|
|
@@ -6113,13 +6097,13 @@ function Builder({
|
|
|
6113
6097
|
onMetaChange: setMeta,
|
|
6114
6098
|
changes: state.changes,
|
|
6115
6099
|
showErrors,
|
|
6116
|
-
actions: /* @__PURE__ */
|
|
6100
|
+
actions: /* @__PURE__ */ jsx17(ElementActions, { editor, selection: state.selection }),
|
|
6117
6101
|
services,
|
|
6118
6102
|
ai,
|
|
6119
6103
|
variables
|
|
6120
6104
|
}
|
|
6121
6105
|
);
|
|
6122
|
-
return /* @__PURE__ */
|
|
6106
|
+
return /* @__PURE__ */ jsxs16(
|
|
6123
6107
|
"div",
|
|
6124
6108
|
{
|
|
6125
6109
|
ref: rootRef,
|
|
@@ -6129,7 +6113,7 @@ function Builder({
|
|
|
6129
6113
|
"data-layout": layout2,
|
|
6130
6114
|
"data-template-id": template.id || "",
|
|
6131
6115
|
children: [
|
|
6132
|
-
/* @__PURE__ */
|
|
6116
|
+
/* @__PURE__ */ jsx17(
|
|
6133
6117
|
Toolbar,
|
|
6134
6118
|
{
|
|
6135
6119
|
title: meta.name,
|
|
@@ -6145,19 +6129,19 @@ function Builder({
|
|
|
6145
6129
|
compact,
|
|
6146
6130
|
actions: [
|
|
6147
6131
|
// The AI module's button first, when the host gave the module.
|
|
6148
|
-
ai?.proofread && /* @__PURE__ */
|
|
6132
|
+
ai?.proofread && /* @__PURE__ */ jsx17(ai.components.Proofread, { context: featureContext, proofread: ai.proofread }, "ai-proofread"),
|
|
6149
6133
|
...toolbarFeatures.map((feature) => {
|
|
6150
6134
|
const Action = feature.toolbar;
|
|
6151
|
-
return /* @__PURE__ */
|
|
6135
|
+
return /* @__PURE__ */ jsx17(Action, { context: featureContext }, feature.id);
|
|
6152
6136
|
})
|
|
6153
6137
|
].filter(Boolean),
|
|
6154
|
-
primary: /* @__PURE__ */
|
|
6155
|
-
saving === "manual" ? /* @__PURE__ */
|
|
6138
|
+
primary: /* @__PURE__ */ jsxs16(Button, { size: "sm-compact", onClick: () => save(), disabled: !ready || saving !== null, children: [
|
|
6139
|
+
saving === "manual" ? /* @__PURE__ */ jsx17(Loader25, { className: "eb:animate-spin" }) : /* @__PURE__ */ jsx17(Save, {}),
|
|
6156
6140
|
t("Save")
|
|
6157
6141
|
] })
|
|
6158
6142
|
}
|
|
6159
6143
|
),
|
|
6160
|
-
/* @__PURE__ */
|
|
6144
|
+
/* @__PURE__ */ jsxs16(
|
|
6161
6145
|
"div",
|
|
6162
6146
|
{
|
|
6163
6147
|
className: cn(
|
|
@@ -6167,12 +6151,12 @@ function Builder({
|
|
|
6167
6151
|
layout2 === "narrow" && "eb:grid-cols-1"
|
|
6168
6152
|
),
|
|
6169
6153
|
children: [
|
|
6170
|
-
!blocksInDrawer && /* @__PURE__ */
|
|
6171
|
-
/* @__PURE__ */
|
|
6172
|
-
/* @__PURE__ */
|
|
6173
|
-
/* @__PURE__ */
|
|
6174
|
-
/* @__PURE__ */
|
|
6175
|
-
engineError && /* @__PURE__ */
|
|
6154
|
+
!blocksInDrawer && /* @__PURE__ */ jsx17(BlockSidebar, { editor, disabled: !ready, onAdded: compact ? () => setDrawer("properties") : void 0 }),
|
|
6155
|
+
/* @__PURE__ */ jsx17("div", { className: "eb:relative eb:flex eb:min-w-0 eb:flex-col eb:bg-surface-75", "aria-busy": !ready, children: boot.status === "error" || engineError ? /* @__PURE__ */ jsxs16("div", { role: "alert", className: "eb:flex eb:h-full eb:flex-col eb:items-center eb:justify-center eb:gap-2 eb:p-6 eb:text-center", children: [
|
|
6156
|
+
/* @__PURE__ */ jsx17(AlertTriangle, { className: "eb:size-6 eb:text-destructive", "aria-hidden": "true" }),
|
|
6157
|
+
/* @__PURE__ */ jsx17("p", { className: "eb:text-[13px] eb:text-foreground", children: boot.status === "error" ? t("The template could not be loaded.") : t("The builder could not start.") }),
|
|
6158
|
+
/* @__PURE__ */ jsx17("p", { className: "eb:max-w-sm eb:text-[12px] eb:text-foreground-muted", children: boot.message || engineError }),
|
|
6159
|
+
engineError && /* @__PURE__ */ jsx17(
|
|
6176
6160
|
Button,
|
|
6177
6161
|
{
|
|
6178
6162
|
variant: "outline",
|
|
@@ -6184,12 +6168,12 @@ function Builder({
|
|
|
6184
6168
|
children: t("Retry")
|
|
6185
6169
|
}
|
|
6186
6170
|
)
|
|
6187
|
-
] }) : /* @__PURE__ */
|
|
6171
|
+
] }) : /* @__PURE__ */ jsxs16(Fragment8, { children: [
|
|
6188
6172
|
canvasBars.map((feature) => {
|
|
6189
6173
|
const Bar = feature.canvasBar;
|
|
6190
|
-
return /* @__PURE__ */
|
|
6174
|
+
return /* @__PURE__ */ jsx17(Bar, { context: featureContext }, feature.id);
|
|
6191
6175
|
}),
|
|
6192
|
-
/* @__PURE__ */
|
|
6176
|
+
/* @__PURE__ */ jsx17("div", { className: "eb:relative eb:min-h-0 eb:flex-1", children: boot.status === "ready" && /* @__PURE__ */ jsx17(
|
|
6193
6177
|
EmailCanvas,
|
|
6194
6178
|
{
|
|
6195
6179
|
editor,
|
|
@@ -6199,14 +6183,14 @@ function Builder({
|
|
|
6199
6183
|
},
|
|
6200
6184
|
attempt
|
|
6201
6185
|
) }),
|
|
6202
|
-
!ready && /* @__PURE__ */
|
|
6203
|
-
/* @__PURE__ */
|
|
6204
|
-
/* @__PURE__ */
|
|
6186
|
+
!ready && /* @__PURE__ */ jsxs16("div", { className: "eb:absolute eb:inset-0 eb:flex eb:items-center eb:justify-center eb:bg-surface-75", role: "status", "aria-live": "polite", children: [
|
|
6187
|
+
/* @__PURE__ */ jsx17(Loader25, { className: "eb:size-6 eb:animate-spin eb:text-foreground-muted", "aria-hidden": "true" }),
|
|
6188
|
+
/* @__PURE__ */ jsx17("span", { className: "eb:sr-only", children: t("Loading the builder") })
|
|
6205
6189
|
] })
|
|
6206
6190
|
] }) }),
|
|
6207
6191
|
!compact && properties,
|
|
6208
|
-
blocksInDrawer && /* @__PURE__ */
|
|
6209
|
-
compact && /* @__PURE__ */
|
|
6192
|
+
blocksInDrawer && /* @__PURE__ */ jsx17(Drawer, { open: drawer === "blocks", side: "left", title: t("Add a block"), onClose: closeDrawer, testId: "eb-blocks-drawer", children: /* @__PURE__ */ jsx17(BlockSidebar, { editor, disabled: !ready, onAdded: () => setDrawer("properties") }) }),
|
|
6193
|
+
compact && /* @__PURE__ */ jsx17(
|
|
6210
6194
|
Drawer,
|
|
6211
6195
|
{
|
|
6212
6196
|
open: drawer === "properties",
|
|
@@ -6221,12 +6205,12 @@ function Builder({
|
|
|
6221
6205
|
]
|
|
6222
6206
|
}
|
|
6223
6207
|
),
|
|
6224
|
-
compact && /* @__PURE__ */
|
|
6225
|
-
blocksInDrawer && /* @__PURE__ */
|
|
6226
|
-
/* @__PURE__ */
|
|
6208
|
+
compact && /* @__PURE__ */ jsxs16("div", { className: "eb:flex eb:shrink-0 eb:items-center eb:gap-2 eb:border-t eb:border-border eb:px-2 eb:py-2", "data-testid": "eb-compact-bar", children: [
|
|
6209
|
+
blocksInDrawer && /* @__PURE__ */ jsxs16(Button, { variant: "outline", size: "sm-compact", disabled: !ready, onClick: () => setDrawer("blocks"), children: [
|
|
6210
|
+
/* @__PURE__ */ jsx17(Plus3, {}),
|
|
6227
6211
|
t("Add a block")
|
|
6228
6212
|
] }),
|
|
6229
|
-
/* @__PURE__ */
|
|
6213
|
+
/* @__PURE__ */ jsxs16(
|
|
6230
6214
|
Button,
|
|
6231
6215
|
{
|
|
6232
6216
|
variant: state.selection ? "default" : "outline",
|
|
@@ -6235,14 +6219,14 @@ function Builder({
|
|
|
6235
6219
|
disabled: !ready,
|
|
6236
6220
|
onClick: () => setDrawer("properties"),
|
|
6237
6221
|
children: [
|
|
6238
|
-
/* @__PURE__ */
|
|
6239
|
-
/* @__PURE__ */
|
|
6222
|
+
/* @__PURE__ */ jsx17(SlidersHorizontal, {}),
|
|
6223
|
+
/* @__PURE__ */ jsx17("span", { className: "eb:truncate", children: ready && state.selection ? t("Edit: {element}", { element: t(state.selection.label) }) : t("Email settings") })
|
|
6240
6224
|
]
|
|
6241
6225
|
}
|
|
6242
6226
|
)
|
|
6243
6227
|
] }),
|
|
6244
|
-
/* @__PURE__ */
|
|
6245
|
-
ai?.key && ai.components.Connect && /* @__PURE__ */
|
|
6228
|
+
/* @__PURE__ */ jsx17(NoticeArea, {}),
|
|
6229
|
+
ai?.key && ai.components.Connect && /* @__PURE__ */ jsx17(ai.components.Connect, { aiKey: ai.key })
|
|
6246
6230
|
]
|
|
6247
6231
|
}
|
|
6248
6232
|
);
|
|
@@ -6253,7 +6237,7 @@ function EmailBuilder({ locale = "en", messages, onNotify, ai: aiProp = null, ..
|
|
|
6253
6237
|
() => ai ? { ...messagesOf(ai.messages, locale), ...messages } : messages,
|
|
6254
6238
|
[ai, locale, messages]
|
|
6255
6239
|
);
|
|
6256
|
-
return /* @__PURE__ */
|
|
6240
|
+
return /* @__PURE__ */ jsx17(UiProvider, { locale, messages: allMessages, onNotify, children: /* @__PURE__ */ jsx17(Builder, { ...props, ai, templateId: props.templateId ?? null }) });
|
|
6257
6241
|
}
|
|
6258
6242
|
function useAiModule(value) {
|
|
6259
6243
|
const valid = Boolean(value && typeof value === "object" && /** @type {any} */
|
|
@@ -6272,7 +6256,7 @@ function useAiModule(value) {
|
|
|
6272
6256
|
// src/ui/EmailBuilderBoundary.jsx
|
|
6273
6257
|
import { Component } from "react";
|
|
6274
6258
|
import { AlertTriangle as AlertTriangle2, ArrowLeft as ArrowLeft3, RefreshCw } from "lucide-react";
|
|
6275
|
-
import { jsx as
|
|
6259
|
+
import { jsx as jsx18, jsxs as jsxs17 } from "react/jsx-runtime";
|
|
6276
6260
|
var isChunkError = (error) => error?.name === "ChunkLoadError" || /Loading (CSS )?chunk .* failed/i.test(String(error?.message || ""));
|
|
6277
6261
|
var english = (text) => text;
|
|
6278
6262
|
var EmailBuilderBoundary = class extends Component {
|
|
@@ -6297,23 +6281,23 @@ var EmailBuilderBoundary = class extends Component {
|
|
|
6297
6281
|
const { error, attempt } = this.state;
|
|
6298
6282
|
const t = this.props.t || english;
|
|
6299
6283
|
if (!error) {
|
|
6300
|
-
return /* @__PURE__ */
|
|
6284
|
+
return /* @__PURE__ */ jsx18("div", { style: { height: "100%" }, children: this.props.children }, attempt);
|
|
6301
6285
|
}
|
|
6302
|
-
return /* @__PURE__ */
|
|
6286
|
+
return /* @__PURE__ */ jsxs17(
|
|
6303
6287
|
"div",
|
|
6304
6288
|
{
|
|
6305
6289
|
role: "alert",
|
|
6306
6290
|
className: "eb-root eb:flex eb:h-full eb:flex-col eb:items-center eb:justify-center eb:gap-3 eb:p-6 eb:text-center",
|
|
6307
6291
|
children: [
|
|
6308
|
-
/* @__PURE__ */
|
|
6309
|
-
/* @__PURE__ */
|
|
6310
|
-
/* @__PURE__ */
|
|
6311
|
-
this.props.onClose && /* @__PURE__ */
|
|
6312
|
-
/* @__PURE__ */
|
|
6292
|
+
/* @__PURE__ */ jsx18(AlertTriangle2, { className: "eb:size-6 eb:text-destructive", "aria-hidden": "true" }),
|
|
6293
|
+
/* @__PURE__ */ jsx18("p", { className: "eb:text-[13px] eb:text-foreground", children: t("The builder could not load.") }),
|
|
6294
|
+
/* @__PURE__ */ jsxs17("div", { className: "eb:flex eb:gap-2", children: [
|
|
6295
|
+
this.props.onClose && /* @__PURE__ */ jsxs17(Button, { variant: "outline", size: "sm", onClick: this.props.onClose, children: [
|
|
6296
|
+
/* @__PURE__ */ jsx18(ArrowLeft3, {}),
|
|
6313
6297
|
t("Back")
|
|
6314
6298
|
] }),
|
|
6315
|
-
/* @__PURE__ */
|
|
6316
|
-
/* @__PURE__ */
|
|
6299
|
+
/* @__PURE__ */ jsxs17(Button, { size: "sm", onClick: this.retry, children: [
|
|
6300
|
+
/* @__PURE__ */ jsx18(RefreshCw, {}),
|
|
6317
6301
|
t("Retry")
|
|
6318
6302
|
] })
|
|
6319
6303
|
] })
|
|
@@ -6341,4 +6325,4 @@ export {
|
|
|
6341
6325
|
EmailBuilder,
|
|
6342
6326
|
EmailBuilderBoundary
|
|
6343
6327
|
};
|
|
6344
|
-
//# sourceMappingURL=chunk-
|
|
6328
|
+
//# sourceMappingURL=chunk-2ZRGQFX6.js.map
|