@kgalexander/mcreate 0.0.10 → 0.0.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-WO43X63M.mjs → chunk-PL5EKNN6.mjs} +37 -30
- package/dist/{core-EII6HF7K.mjs → core-UL57QVFL.mjs} +1 -1
- package/dist/index.d.mts +5 -3
- package/dist/index.d.ts +5 -3
- package/dist/index.js +619 -609
- package/dist/index.mjs +19 -9
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -267,7 +267,7 @@ var init_format = __esm({
|
|
|
267
267
|
function propertyCardMockMjml(block, context) {
|
|
268
268
|
const a = block.attributes;
|
|
269
269
|
const trackingClasses = context.mode === "editing" ? getTrackingClasses(context.idx, "property-card") : "";
|
|
270
|
-
const href = a["href"] ||
|
|
270
|
+
const href = a["href"] || "";
|
|
271
271
|
const price = formatPrice(a["price"] || "$0");
|
|
272
272
|
const address = a["address"] || "123 Main Street";
|
|
273
273
|
const city = a["city"] || "City, ST 00000";
|
|
@@ -443,7 +443,7 @@ function propertyCardSingleTwoMockMjml(block, context) {
|
|
|
443
443
|
const a = block.attributes;
|
|
444
444
|
const trackingClasses = context.mode === "editing" ? getTrackingClasses(context.idx, "property-card-single-two") : "";
|
|
445
445
|
const uniqueId = Math.random().toString(36).slice(2, 8);
|
|
446
|
-
const href = a["href"] ||
|
|
446
|
+
const href = a["href"] || "";
|
|
447
447
|
const price = formatPrice(a["price"] || "$0");
|
|
448
448
|
const address = a["address"] || "123 Main Street";
|
|
449
449
|
const city = a["city"] || "City, ST 00000";
|
|
@@ -644,7 +644,7 @@ var init_mock_property_singletwo = __esm({
|
|
|
644
644
|
// src/render/Mockup/mock-property-triple-better.ts
|
|
645
645
|
function renderCard(child, childIdx, context, uniqueId, borderRadius, imageHeight, border, fontFamily, textColor, backgroundColor, innerBorderRadius) {
|
|
646
646
|
const attrs = child.attributes;
|
|
647
|
-
const href = attrs["href"] ||
|
|
647
|
+
const href = attrs["href"] || "";
|
|
648
648
|
const price = formatPrice(attrs["price"] || "$0");
|
|
649
649
|
const beds = formatNumber(attrs["beds"] || "--");
|
|
650
650
|
const baths = formatNumber(attrs["baths"] || "--");
|
|
@@ -948,7 +948,7 @@ function propertyCardTripleToMjml(block, context) {
|
|
|
948
948
|
const cardNum = index + 1;
|
|
949
949
|
const childAttrs = child.attributes || {};
|
|
950
950
|
productionAttrs[`image-src-${cardNum}`] = childAttrs["image-src"] || "";
|
|
951
|
-
productionAttrs[`href-${cardNum}`] = childAttrs["href"] ||
|
|
951
|
+
productionAttrs[`href-${cardNum}`] = childAttrs["href"] || "";
|
|
952
952
|
productionAttrs[`price-${cardNum}`] = childAttrs["price"] || "$0";
|
|
953
953
|
productionAttrs[`beds-${cardNum}`] = childAttrs["beds"] || "--";
|
|
954
954
|
productionAttrs[`baths-${cardNum}`] = childAttrs["baths"] || "--";
|
|
@@ -1076,7 +1076,7 @@ function rootChildrenToMjml(children, parentContext) {
|
|
|
1076
1076
|
function json2mjml(template, mode = "production", options = {}) {
|
|
1077
1077
|
const showCompanyFooter = template.content[0]?.data?.value?.showCompanyFooter ?? true;
|
|
1078
1078
|
const footerMutation = needsCompanyFooterMutation(template);
|
|
1079
|
-
const needsProductionFilter = mode === "production" && options?.
|
|
1079
|
+
const needsProductionFilter = mode === "production" && (options?.isPaidLevel ?? 0) >= 1 && !showCompanyFooter;
|
|
1080
1080
|
if (footerMutation || needsProductionFilter) {
|
|
1081
1081
|
template = typeof structuredClone !== "undefined" ? structuredClone(template) : JSON.parse(JSON.stringify(template));
|
|
1082
1082
|
}
|
|
@@ -1129,7 +1129,6 @@ function json2mjml(template, mode = "production", options = {}) {
|
|
|
1129
1129
|
p { margin: 0px 0px 0px 0px !important; }
|
|
1130
1130
|
a { color: ${linkColor}; text-decoration: none; }
|
|
1131
1131
|
a span { text-decoration: underline; }
|
|
1132
|
-
p span { text-decoration: underline; }
|
|
1133
1132
|
|
|
1134
1133
|
.property-details-separator {font-family:Arial,sans-serif;color:#C3C3C3;padding:0 4px 0 2px;}
|
|
1135
1134
|
|
|
@@ -1492,7 +1491,7 @@ function createPropertyCardElement(payload) {
|
|
|
1492
1491
|
"beds": "--",
|
|
1493
1492
|
"baths": "--",
|
|
1494
1493
|
"sqft": "--",
|
|
1495
|
-
"
|
|
1494
|
+
"href": "",
|
|
1496
1495
|
"status": "Empty",
|
|
1497
1496
|
"is-status": "show",
|
|
1498
1497
|
"status-color": "#B8B8B8",
|
|
@@ -1521,7 +1520,7 @@ function createPropertyCardSingleTwoElement(payload) {
|
|
|
1521
1520
|
"beds": "--",
|
|
1522
1521
|
"baths": "--",
|
|
1523
1522
|
"sqft": "--",
|
|
1524
|
-
"
|
|
1523
|
+
"href": "",
|
|
1525
1524
|
"status": "Empty",
|
|
1526
1525
|
"is-status": "show",
|
|
1527
1526
|
"status-color": "#B8B8B8",
|
|
@@ -1608,7 +1607,7 @@ function createPropertyCardTripleItemElement(payload) {
|
|
|
1608
1607
|
data: { value: {} },
|
|
1609
1608
|
attributes: {
|
|
1610
1609
|
"image-src": payload?.attributes?.["image-src"] || "https://cornerstonepropertymgmt.com/wp-content/themes/cornerstone/assets/img/nofeaturedimage.jpg",
|
|
1611
|
-
"
|
|
1610
|
+
"href": payload?.attributes?.["href"] || "",
|
|
1612
1611
|
"price": payload?.attributes?.["price"] || "$0",
|
|
1613
1612
|
"beds": payload?.attributes?.["beds"] || "--",
|
|
1614
1613
|
"baths": payload?.attributes?.["baths"] || "--",
|
|
@@ -1628,6 +1627,7 @@ function createPropertyCardTripleElement(payload) {
|
|
|
1628
1627
|
"gap": "24px",
|
|
1629
1628
|
"border-radius": "0px",
|
|
1630
1629
|
"border": "1px solid #d1d1d5",
|
|
1630
|
+
"background-color": "#ffffff",
|
|
1631
1631
|
"image-height": "102px",
|
|
1632
1632
|
...payload?.attributes
|
|
1633
1633
|
},
|
|
@@ -2015,7 +2015,7 @@ function setValueAtPath(template, path, value) {
|
|
|
2015
2015
|
(0, import_set.default)(template, lodashPath, value);
|
|
2016
2016
|
}
|
|
2017
2017
|
}
|
|
2018
|
-
var import_zustand, import_immer, import_middleware, import_get2, import_set, import_cloneDeep, import_immer2, import_sonner, SECTION_INDEX_REGEX, MAX_TEMPLATE_SIZE, defaultTemplate, useEditorStore;
|
|
2018
|
+
var import_zustand, import_immer, import_middleware, import_get2, import_set, import_cloneDeep, import_immer2, import_sonner, SECTION_INDEX_REGEX, MAX_TEMPLATE_SIZE, defaultTemplate, MAX_PAID_LEVEL, useEditorStore;
|
|
2019
2019
|
var init_editor = __esm({
|
|
2020
2020
|
"src/core/editor/state/editor.ts"() {
|
|
2021
2021
|
"use strict";
|
|
@@ -2040,6 +2040,7 @@ var init_editor = __esm({
|
|
|
2040
2040
|
id: page.id || generateId()
|
|
2041
2041
|
}))
|
|
2042
2042
|
};
|
|
2043
|
+
MAX_PAID_LEVEL = 3;
|
|
2043
2044
|
useEditorStore = (0, import_zustand.create)()(
|
|
2044
2045
|
(0, import_middleware.devtools)(
|
|
2045
2046
|
(0, import_immer.immer)((set) => ({
|
|
@@ -2068,12 +2069,15 @@ var init_editor = __esm({
|
|
|
2068
2069
|
// Auto-save tracking
|
|
2069
2070
|
lastSavedSnapshot: null,
|
|
2070
2071
|
isSaving: false,
|
|
2072
|
+
// Subscription level (0 = free, 1-3 = paid tiers)
|
|
2073
|
+
isPaidLevel: 0,
|
|
2071
2074
|
// Initialize store with external template (for npm package usage)
|
|
2072
|
-
initializeWithTemplate: (templateId, template, onSave) => {
|
|
2075
|
+
initializeWithTemplate: (templateId, template, onSave, isPaidLevel) => {
|
|
2073
2076
|
set((state) => {
|
|
2074
2077
|
state.templateId = templateId;
|
|
2075
2078
|
state.template = template;
|
|
2076
2079
|
state.onSave = onSave ?? null;
|
|
2080
|
+
state.isPaidLevel = isPaidLevel ?? 0;
|
|
2077
2081
|
state.templateSize = calculateTemplateSize(template);
|
|
2078
2082
|
state.isAtSizeLimit = false;
|
|
2079
2083
|
state.history = [(0, import_cloneDeep.default)(template)];
|
|
@@ -3613,579 +3617,6 @@ function getEditorStyles(isDragButtonHovered, textEditingIdx) {
|
|
|
3613
3617
|
cssCache.set(cacheKey, css);
|
|
3614
3618
|
return css;
|
|
3615
3619
|
}
|
|
3616
|
-
function ShadowDomRenderer({
|
|
3617
|
-
html,
|
|
3618
|
-
focusIdx,
|
|
3619
|
-
hoverIdx,
|
|
3620
|
-
dropIndicator,
|
|
3621
|
-
dropTargetIdx,
|
|
3622
|
-
isDragging = false,
|
|
3623
|
-
isDragButtonHovered = false,
|
|
3624
|
-
textEditingIdx = null,
|
|
3625
|
-
showCompanyFooter = true,
|
|
3626
|
-
onElementClick,
|
|
3627
|
-
onElementHover,
|
|
3628
|
-
onContentInput,
|
|
3629
|
-
onEditingStart,
|
|
3630
|
-
onEditingEnd,
|
|
3631
|
-
onDragOver,
|
|
3632
|
-
onDrop,
|
|
3633
|
-
onDragLeave,
|
|
3634
|
-
onSelectionRectChange,
|
|
3635
|
-
onShadowRootRef,
|
|
3636
|
-
onSlashCommand,
|
|
3637
|
-
onSlashCommandClose,
|
|
3638
|
-
onTextEditStart
|
|
3639
|
-
}) {
|
|
3640
|
-
const containerRef = (0, import_react.useRef)(null);
|
|
3641
|
-
const shadowRootRef = (0, import_react.useRef)(null);
|
|
3642
|
-
const rafRef = (0, import_react.useRef)(null);
|
|
3643
|
-
const lastBlockNodeRef = (0, import_react.useRef)(null);
|
|
3644
|
-
const callbackRefs = (0, import_react.useRef)({
|
|
3645
|
-
onElementClick,
|
|
3646
|
-
onElementHover,
|
|
3647
|
-
onContentInput,
|
|
3648
|
-
onEditingStart,
|
|
3649
|
-
onEditingEnd,
|
|
3650
|
-
onDragOver,
|
|
3651
|
-
onDrop,
|
|
3652
|
-
onDragLeave,
|
|
3653
|
-
onSlashCommand,
|
|
3654
|
-
onSlashCommandClose,
|
|
3655
|
-
onTextEditStart
|
|
3656
|
-
});
|
|
3657
|
-
(0, import_react.useEffect)(() => {
|
|
3658
|
-
callbackRefs.current = {
|
|
3659
|
-
onElementClick,
|
|
3660
|
-
onElementHover,
|
|
3661
|
-
onContentInput,
|
|
3662
|
-
onEditingStart,
|
|
3663
|
-
onEditingEnd,
|
|
3664
|
-
onDragOver,
|
|
3665
|
-
onDrop,
|
|
3666
|
-
onDragLeave,
|
|
3667
|
-
onSlashCommand,
|
|
3668
|
-
onSlashCommandClose,
|
|
3669
|
-
onTextEditStart
|
|
3670
|
-
};
|
|
3671
|
-
});
|
|
3672
|
-
(0, import_react.useEffect)(() => {
|
|
3673
|
-
if (containerRef.current && !shadowRootRef.current) {
|
|
3674
|
-
shadowRootRef.current = containerRef.current.attachShadow({ mode: "open" });
|
|
3675
|
-
onShadowRootRef?.(shadowRootRef.current);
|
|
3676
|
-
}
|
|
3677
|
-
}, [onShadowRootRef]);
|
|
3678
|
-
(0, import_react.useEffect)(() => {
|
|
3679
|
-
if (!shadowRootRef.current) return;
|
|
3680
|
-
const styleEl = document.createElement("style");
|
|
3681
|
-
styleEl.id = "editor-styles";
|
|
3682
|
-
styleEl.textContent = getEditorStyles(isDragButtonHovered, textEditingIdx);
|
|
3683
|
-
const contentWrapper = document.createElement("div");
|
|
3684
|
-
contentWrapper.className = "shadow-content";
|
|
3685
|
-
contentWrapper.innerHTML = html;
|
|
3686
|
-
contentWrapper.style.setProperty("--company-footer-opacity", showCompanyFooter ? "1" : "0.25");
|
|
3687
|
-
contentWrapper.style.setProperty("--outline-color", isDragging ? "rgb(147, 51, 234)" : "rgb(59, 130, 246)");
|
|
3688
|
-
contentWrapper.style.setProperty("--outline-color-hover", isDragging ? "rgba(147, 51, 234, 0.5)" : "rgba(59, 130, 246, 0.5)");
|
|
3689
|
-
const template = useEditorStore.getState().template;
|
|
3690
|
-
const linkColor2 = template?.content?.[0]?.data?.value?.linkColor || "#0000ff";
|
|
3691
|
-
contentWrapper.style.setProperty("--link-color", linkColor2);
|
|
3692
|
-
injectContentEditable(contentWrapper);
|
|
3693
|
-
shadowRootRef.current.innerHTML = "";
|
|
3694
|
-
shadowRootRef.current.appendChild(styleEl);
|
|
3695
|
-
shadowRootRef.current.appendChild(contentWrapper);
|
|
3696
|
-
}, [html]);
|
|
3697
|
-
(0, import_react.useEffect)(() => {
|
|
3698
|
-
if (!shadowRootRef.current) return;
|
|
3699
|
-
const styleEl = shadowRootRef.current.querySelector("#editor-styles");
|
|
3700
|
-
if (styleEl) {
|
|
3701
|
-
styleEl.textContent = getEditorStyles(isDragButtonHovered, textEditingIdx);
|
|
3702
|
-
}
|
|
3703
|
-
}, [isDragButtonHovered, textEditingIdx]);
|
|
3704
|
-
(0, import_react.useEffect)(() => {
|
|
3705
|
-
if (!shadowRootRef.current) return;
|
|
3706
|
-
const contentWrapper = shadowRootRef.current.querySelector(".shadow-content");
|
|
3707
|
-
if (contentWrapper) {
|
|
3708
|
-
contentWrapper.style.setProperty("--outline-color", isDragging ? "rgb(147, 51, 234)" : "rgb(59, 130, 246)");
|
|
3709
|
-
contentWrapper.style.setProperty("--outline-color-hover", isDragging ? "rgba(147, 51, 234, 0.5)" : "rgba(59, 130, 246, 0.5)");
|
|
3710
|
-
contentWrapper.style.setProperty("--company-footer-opacity", showCompanyFooter ? "1" : "0.25");
|
|
3711
|
-
const template = useEditorStore.getState().template;
|
|
3712
|
-
const linkColor2 = template?.content?.[0]?.data?.value?.linkColor || "#0000ff";
|
|
3713
|
-
contentWrapper.style.setProperty("--link-color", linkColor2);
|
|
3714
|
-
}
|
|
3715
|
-
}, [isDragging, showCompanyFooter]);
|
|
3716
|
-
const linkColor = useEditorStore((state) => state.template?.content?.[0]?.data?.value?.linkColor);
|
|
3717
|
-
(0, import_react.useEffect)(() => {
|
|
3718
|
-
if (!shadowRootRef.current) return;
|
|
3719
|
-
const contentWrapper = shadowRootRef.current.querySelector(".shadow-content");
|
|
3720
|
-
if (contentWrapper) {
|
|
3721
|
-
contentWrapper.style.setProperty("--link-color", linkColor || "#0000ff");
|
|
3722
|
-
}
|
|
3723
|
-
}, [linkColor]);
|
|
3724
|
-
(0, import_react.useEffect)(() => {
|
|
3725
|
-
if (!shadowRootRef.current) return;
|
|
3726
|
-
shadowRootRef.current.querySelectorAll(".is-selected").forEach((el) => {
|
|
3727
|
-
el.classList.remove("is-selected");
|
|
3728
|
-
});
|
|
3729
|
-
shadowRootRef.current.querySelectorAll(".is-column-selected").forEach((el) => {
|
|
3730
|
-
el.classList.remove("is-column-selected");
|
|
3731
|
-
});
|
|
3732
|
-
shadowRootRef.current.querySelectorAll(".is-card-selected").forEach((el) => {
|
|
3733
|
-
el.classList.remove("is-card-selected");
|
|
3734
|
-
});
|
|
3735
|
-
if (focusIdx) {
|
|
3736
|
-
const selectedEl = shadowRootRef.current.querySelector(
|
|
3737
|
-
`.node-idx-${CSS.escape(focusIdx)}`
|
|
3738
|
-
);
|
|
3739
|
-
if (selectedEl) {
|
|
3740
|
-
if (focusIdx !== "content") {
|
|
3741
|
-
selectedEl.classList.add("is-selected");
|
|
3742
|
-
}
|
|
3743
|
-
if (selectedEl.classList.contains("node-type-section-column")) {
|
|
3744
|
-
selectedEl.querySelectorAll(".node-type-column").forEach((col) => {
|
|
3745
|
-
col.classList.add("is-column-selected");
|
|
3746
|
-
});
|
|
3747
|
-
} else if (selectedEl.classList.contains("node-type-column")) {
|
|
3748
|
-
const parentSection = selectedEl.closest(".node-type-section-column");
|
|
3749
|
-
if (parentSection) {
|
|
3750
|
-
parentSection.querySelectorAll(".node-type-column").forEach((col) => {
|
|
3751
|
-
col.classList.add("is-column-selected");
|
|
3752
|
-
});
|
|
3753
|
-
}
|
|
3754
|
-
} else {
|
|
3755
|
-
const parentSectionColumn = selectedEl.closest(".node-type-section-column");
|
|
3756
|
-
if (parentSectionColumn) {
|
|
3757
|
-
const parentColumn = selectedEl.closest(".node-type-column");
|
|
3758
|
-
if (parentColumn) {
|
|
3759
|
-
parentColumn.classList.add("is-column-selected");
|
|
3760
|
-
}
|
|
3761
|
-
}
|
|
3762
|
-
}
|
|
3763
|
-
if (selectedEl.classList.contains("node-type-section-property-triple")) {
|
|
3764
|
-
selectedEl.querySelectorAll(".node-type-property-card-triple-item").forEach((card) => {
|
|
3765
|
-
card.classList.add("is-card-selected");
|
|
3766
|
-
});
|
|
3767
|
-
} else if (selectedEl.classList.contains("node-type-property-card-triple")) {
|
|
3768
|
-
selectedEl.querySelectorAll(".node-type-property-card-triple-item").forEach((card) => {
|
|
3769
|
-
card.classList.add("is-card-selected");
|
|
3770
|
-
});
|
|
3771
|
-
} else if (selectedEl.classList.contains("node-type-property-card-triple-item")) {
|
|
3772
|
-
selectedEl.classList.add("is-card-selected");
|
|
3773
|
-
}
|
|
3774
|
-
onSelectionRectChange?.(selectedEl.getBoundingClientRect());
|
|
3775
|
-
} else {
|
|
3776
|
-
onSelectionRectChange?.(null);
|
|
3777
|
-
}
|
|
3778
|
-
} else {
|
|
3779
|
-
onSelectionRectChange?.(null);
|
|
3780
|
-
}
|
|
3781
|
-
}, [focusIdx, html, onSelectionRectChange]);
|
|
3782
|
-
(0, import_react.useEffect)(() => {
|
|
3783
|
-
if (!shadowRootRef.current) return;
|
|
3784
|
-
shadowRootRef.current.querySelectorAll(".is-hovered").forEach((el) => {
|
|
3785
|
-
el.classList.remove("is-hovered");
|
|
3786
|
-
});
|
|
3787
|
-
shadowRootRef.current.querySelectorAll(".is-column-hover-parent").forEach((el) => {
|
|
3788
|
-
el.classList.remove("is-column-hover-parent");
|
|
3789
|
-
});
|
|
3790
|
-
if (hoverIdx && hoverIdx !== focusIdx && hoverIdx !== "content") {
|
|
3791
|
-
const hoveredEl = shadowRootRef.current.querySelector(
|
|
3792
|
-
`.node-idx-${CSS.escape(hoverIdx)}`
|
|
3793
|
-
);
|
|
3794
|
-
if (hoveredEl) {
|
|
3795
|
-
hoveredEl.classList.add("is-hovered");
|
|
3796
|
-
if (!hoveredEl.classList.contains("node-type-column")) {
|
|
3797
|
-
const parentSectionColumn = hoveredEl.closest(".node-type-section-column");
|
|
3798
|
-
if (parentSectionColumn) {
|
|
3799
|
-
const parentColumn = hoveredEl.closest(".node-type-column");
|
|
3800
|
-
if (parentColumn) {
|
|
3801
|
-
parentColumn.classList.add("is-column-hover-parent");
|
|
3802
|
-
}
|
|
3803
|
-
}
|
|
3804
|
-
}
|
|
3805
|
-
}
|
|
3806
|
-
}
|
|
3807
|
-
}, [focusIdx, hoverIdx, html]);
|
|
3808
|
-
(0, import_react.useEffect)(() => {
|
|
3809
|
-
if (!shadowRootRef.current) return;
|
|
3810
|
-
shadowRootRef.current.querySelectorAll(".is-drop-target").forEach((el) => {
|
|
3811
|
-
el.classList.remove("is-drop-target");
|
|
3812
|
-
});
|
|
3813
|
-
if (dropTargetIdx && isDragging) {
|
|
3814
|
-
const targetEl = shadowRootRef.current.querySelector(
|
|
3815
|
-
`.node-idx-${CSS.escape(dropTargetIdx)}`
|
|
3816
|
-
);
|
|
3817
|
-
if (targetEl) {
|
|
3818
|
-
targetEl.classList.add("is-drop-target");
|
|
3819
|
-
}
|
|
3820
|
-
}
|
|
3821
|
-
}, [dropTargetIdx, isDragging]);
|
|
3822
|
-
(0, import_react.useEffect)(() => {
|
|
3823
|
-
const shadowRoot = shadowRootRef.current;
|
|
3824
|
-
if (!shadowRoot) return;
|
|
3825
|
-
const handleMouseDown = (e) => {
|
|
3826
|
-
const target = e.target;
|
|
3827
|
-
const mouseEvent = e;
|
|
3828
|
-
if (target.closest('[contenteditable="true"]')) {
|
|
3829
|
-
return;
|
|
3830
|
-
}
|
|
3831
|
-
const blockNode = target.closest(`.${EMAIL_BLOCK_CLASS_NAME}`);
|
|
3832
|
-
if (blockNode && callbackRefs.current.onElementClick) {
|
|
3833
|
-
const idx = getIdxFromElement(blockNode);
|
|
3834
|
-
if (idx) {
|
|
3835
|
-
callbackRefs.current.onElementClick(idx);
|
|
3836
|
-
if (blockNode.classList.contains("node-type-text") && callbackRefs.current.onTextEditStart) {
|
|
3837
|
-
const contentDiv = blockNode.querySelector("div");
|
|
3838
|
-
if (contentDiv) {
|
|
3839
|
-
const blockEl = blockNode;
|
|
3840
|
-
const blockRect = blockEl.getBoundingClientRect();
|
|
3841
|
-
const blockStyle = window.getComputedStyle(blockEl);
|
|
3842
|
-
const content = contentDiv.innerHTML;
|
|
3843
|
-
const clickX = mouseEvent.clientX;
|
|
3844
|
-
const clickY = mouseEvent.clientY;
|
|
3845
|
-
const contentStyle = window.getComputedStyle(contentDiv);
|
|
3846
|
-
const styles = {
|
|
3847
|
-
fontFamily: contentStyle.fontFamily,
|
|
3848
|
-
fontSize: contentStyle.fontSize,
|
|
3849
|
-
fontWeight: contentStyle.fontWeight,
|
|
3850
|
-
lineHeight: contentStyle.lineHeight,
|
|
3851
|
-
color: contentStyle.color,
|
|
3852
|
-
textAlign: contentStyle.textAlign,
|
|
3853
|
-
padding: blockStyle.padding,
|
|
3854
|
-
// Use td's padding
|
|
3855
|
-
// Additional text-rendering properties for exact matching
|
|
3856
|
-
letterSpacing: contentStyle.letterSpacing,
|
|
3857
|
-
wordSpacing: contentStyle.wordSpacing,
|
|
3858
|
-
textRendering: contentStyle.textRendering,
|
|
3859
|
-
fontStyle: contentStyle.fontStyle,
|
|
3860
|
-
textDecoration: contentStyle.textDecoration,
|
|
3861
|
-
textTransform: contentStyle.textTransform,
|
|
3862
|
-
fontVariant: contentStyle.fontVariant,
|
|
3863
|
-
textIndent: contentStyle.textIndent
|
|
3864
|
-
};
|
|
3865
|
-
callbackRefs.current.onTextEditStart?.(idx, blockRect.width, blockRect.height, clickX, clickY, content, styles);
|
|
3866
|
-
}
|
|
3867
|
-
}
|
|
3868
|
-
}
|
|
3869
|
-
}
|
|
3870
|
-
};
|
|
3871
|
-
const handleMouseOver = (e) => {
|
|
3872
|
-
const target = e.target;
|
|
3873
|
-
const blockNode = target.closest(`.${EMAIL_BLOCK_CLASS_NAME}`);
|
|
3874
|
-
if (blockNode && callbackRefs.current.onElementHover) {
|
|
3875
|
-
const idx = getIdxFromElement(blockNode);
|
|
3876
|
-
callbackRefs.current.onElementHover(idx || null);
|
|
3877
|
-
}
|
|
3878
|
-
};
|
|
3879
|
-
const handleMouseOut = (e) => {
|
|
3880
|
-
const relatedTarget = e.relatedTarget;
|
|
3881
|
-
if (!relatedTarget || !shadowRoot.contains(relatedTarget)) {
|
|
3882
|
-
callbackRefs.current.onElementHover?.(null);
|
|
3883
|
-
}
|
|
3884
|
-
};
|
|
3885
|
-
const handleInput = (e) => {
|
|
3886
|
-
const target = e.target;
|
|
3887
|
-
if (target.getAttribute("contenteditable") === "true" && callbackRefs.current.onContentInput) {
|
|
3888
|
-
const contentIdx = target.getAttribute("data-content-idx");
|
|
3889
|
-
if (contentIdx) {
|
|
3890
|
-
const isRichText = target.closest(".node-type-text");
|
|
3891
|
-
const content = isRichText ? target.innerHTML : target.textContent || "";
|
|
3892
|
-
callbackRefs.current.onContentInput(contentIdx, content);
|
|
3893
|
-
if (isRichText) {
|
|
3894
|
-
if (isTextContentEmpty(target)) {
|
|
3895
|
-
target.classList.add("is-empty");
|
|
3896
|
-
} else {
|
|
3897
|
-
target.classList.remove("is-empty");
|
|
3898
|
-
}
|
|
3899
|
-
}
|
|
3900
|
-
}
|
|
3901
|
-
}
|
|
3902
|
-
};
|
|
3903
|
-
const handleFocus = (e) => {
|
|
3904
|
-
const target = e.target;
|
|
3905
|
-
if (target.getAttribute("contenteditable") === "true") {
|
|
3906
|
-
callbackRefs.current.onEditingStart?.();
|
|
3907
|
-
const blockNode = target.closest(`.${EMAIL_BLOCK_CLASS_NAME}`);
|
|
3908
|
-
if (blockNode && callbackRefs.current.onElementClick) {
|
|
3909
|
-
const idx = getIdxFromElement(blockNode);
|
|
3910
|
-
if (idx) {
|
|
3911
|
-
callbackRefs.current.onElementClick(idx);
|
|
3912
|
-
}
|
|
3913
|
-
}
|
|
3914
|
-
}
|
|
3915
|
-
};
|
|
3916
|
-
const handleBlur = (e) => {
|
|
3917
|
-
const target = e.target;
|
|
3918
|
-
if (target.getAttribute("contenteditable") === "true") {
|
|
3919
|
-
callbackRefs.current.onEditingEnd?.();
|
|
3920
|
-
}
|
|
3921
|
-
};
|
|
3922
|
-
const getParagraphInfo = (blockNode, mouseY) => {
|
|
3923
|
-
if (!blockNode.classList.contains("node-type-text")) return void 0;
|
|
3924
|
-
const contentDiv = blockNode.querySelector("div");
|
|
3925
|
-
if (!contentDiv) return void 0;
|
|
3926
|
-
const paragraphs = Array.from(contentDiv.querySelectorAll("p"));
|
|
3927
|
-
if (paragraphs.length < 2) return void 0;
|
|
3928
|
-
for (let i = 0; i < paragraphs.length; i++) {
|
|
3929
|
-
const pRect = paragraphs[i].getBoundingClientRect();
|
|
3930
|
-
if (mouseY >= pRect.top && mouseY <= pRect.bottom) {
|
|
3931
|
-
const midpoint = pRect.top + pRect.height / 2;
|
|
3932
|
-
return {
|
|
3933
|
-
paragraphIndex: i,
|
|
3934
|
-
totalParagraphs: paragraphs.length,
|
|
3935
|
-
paragraphRect: pRect,
|
|
3936
|
-
isAboveMidpoint: mouseY < midpoint
|
|
3937
|
-
};
|
|
3938
|
-
}
|
|
3939
|
-
}
|
|
3940
|
-
return void 0;
|
|
3941
|
-
};
|
|
3942
|
-
const handleDragOver = (e) => {
|
|
3943
|
-
const dragEvent = e;
|
|
3944
|
-
dragEvent.preventDefault();
|
|
3945
|
-
const target = dragEvent.target;
|
|
3946
|
-
const mouseY = dragEvent.clientY;
|
|
3947
|
-
const mouseX = dragEvent.clientX;
|
|
3948
|
-
let blockNode = target.closest(`.${EMAIL_BLOCK_CLASS_NAME}`);
|
|
3949
|
-
if (blockNode && getTypeFromElement(blockNode) === "social-item") {
|
|
3950
|
-
blockNode = blockNode.parentElement?.closest(`.${EMAIL_BLOCK_CLASS_NAME}`) || null;
|
|
3951
|
-
}
|
|
3952
|
-
if (rafRef.current) return;
|
|
3953
|
-
rafRef.current = requestAnimationFrame(() => {
|
|
3954
|
-
rafRef.current = null;
|
|
3955
|
-
if (blockNode === lastBlockNodeRef.current && blockNode) {
|
|
3956
|
-
const dragInfo2 = {
|
|
3957
|
-
idx: getIdxFromElement(blockNode),
|
|
3958
|
-
type: getTypeFromElement(blockNode),
|
|
3959
|
-
rect: blockNode.getBoundingClientRect(),
|
|
3960
|
-
mouseY,
|
|
3961
|
-
// Use captured value
|
|
3962
|
-
mouseX,
|
|
3963
|
-
// Use captured value
|
|
3964
|
-
paragraphInfo: getParagraphInfo(blockNode, mouseY)
|
|
3965
|
-
};
|
|
3966
|
-
callbackRefs.current.onDragOver?.(dragEvent, dragInfo2);
|
|
3967
|
-
return;
|
|
3968
|
-
}
|
|
3969
|
-
lastBlockNodeRef.current = blockNode;
|
|
3970
|
-
const dragInfo = {
|
|
3971
|
-
idx: blockNode ? getIdxFromElement(blockNode) : null,
|
|
3972
|
-
type: blockNode ? getTypeFromElement(blockNode) : null,
|
|
3973
|
-
rect: blockNode ? blockNode.getBoundingClientRect() : null,
|
|
3974
|
-
mouseY,
|
|
3975
|
-
// Use captured value
|
|
3976
|
-
mouseX,
|
|
3977
|
-
// Use captured value
|
|
3978
|
-
paragraphInfo: blockNode ? getParagraphInfo(blockNode, mouseY) : void 0
|
|
3979
|
-
};
|
|
3980
|
-
callbackRefs.current.onDragOver?.(dragEvent, dragInfo);
|
|
3981
|
-
});
|
|
3982
|
-
};
|
|
3983
|
-
const handleDragLeave = (e) => {
|
|
3984
|
-
const dragEvent = e;
|
|
3985
|
-
const relatedTarget = dragEvent.relatedTarget;
|
|
3986
|
-
if (!relatedTarget || !shadowRoot.contains(relatedTarget)) {
|
|
3987
|
-
lastBlockNodeRef.current = null;
|
|
3988
|
-
callbackRefs.current.onDragLeave?.();
|
|
3989
|
-
}
|
|
3990
|
-
};
|
|
3991
|
-
const handleDrop = (e) => {
|
|
3992
|
-
const dragEvent = e;
|
|
3993
|
-
dragEvent.preventDefault();
|
|
3994
|
-
const target = dragEvent.target;
|
|
3995
|
-
const mouseY = dragEvent.clientY;
|
|
3996
|
-
const mouseX = dragEvent.clientX;
|
|
3997
|
-
const blockNode = target.closest(`.${EMAIL_BLOCK_CLASS_NAME}`);
|
|
3998
|
-
const dragInfo = {
|
|
3999
|
-
idx: blockNode ? getIdxFromElement(blockNode) : null,
|
|
4000
|
-
type: blockNode ? getTypeFromElement(blockNode) : null,
|
|
4001
|
-
rect: blockNode ? blockNode.getBoundingClientRect() : null,
|
|
4002
|
-
mouseY,
|
|
4003
|
-
mouseX,
|
|
4004
|
-
paragraphInfo: blockNode ? getParagraphInfo(blockNode, mouseY) : void 0
|
|
4005
|
-
};
|
|
4006
|
-
callbackRefs.current.onDrop?.(dragEvent, dragInfo);
|
|
4007
|
-
};
|
|
4008
|
-
const handleLinkClick = (e) => {
|
|
4009
|
-
const target = e.target;
|
|
4010
|
-
if (target.tagName === "A" || target.closest("a")) {
|
|
4011
|
-
e.preventDefault();
|
|
4012
|
-
}
|
|
4013
|
-
};
|
|
4014
|
-
const handleKeyDown = (e) => {
|
|
4015
|
-
const keyEvent = e;
|
|
4016
|
-
const target = keyEvent.target;
|
|
4017
|
-
if (target.getAttribute("contenteditable") === "true" && target.closest(".node-type-button")) {
|
|
4018
|
-
const isAtSizeLimit = useEditorStore.getState().isAtSizeLimit;
|
|
4019
|
-
if (isAtSizeLimit) {
|
|
4020
|
-
const allowedKeys = ["Backspace", "Delete", "ArrowLeft", "ArrowRight", "ArrowUp", "ArrowDown", "Escape", "Tab"];
|
|
4021
|
-
const isModifier = keyEvent.ctrlKey || keyEvent.metaKey || keyEvent.altKey;
|
|
4022
|
-
const isAllowed = allowedKeys.includes(keyEvent.key) || isModifier;
|
|
4023
|
-
if (!isAllowed && (keyEvent.key.length === 1 || keyEvent.key === "Enter")) {
|
|
4024
|
-
keyEvent.preventDefault();
|
|
4025
|
-
const now = Date.now();
|
|
4026
|
-
if (now - lastSizeLimitToastTime > 2e3) {
|
|
4027
|
-
lastSizeLimitToastTime = now;
|
|
4028
|
-
import_sonner2.toast.error("Template size limit reached");
|
|
4029
|
-
}
|
|
4030
|
-
return;
|
|
4031
|
-
}
|
|
4032
|
-
}
|
|
4033
|
-
return;
|
|
4034
|
-
}
|
|
4035
|
-
const relevantKeys = ["/", "Escape", " ", "Backspace"];
|
|
4036
|
-
if (!relevantKeys.includes(keyEvent.key)) return;
|
|
4037
|
-
if (target.getAttribute("contenteditable") !== "true") return;
|
|
4038
|
-
if (!target.closest(".node-type-text")) return;
|
|
4039
|
-
if (keyEvent.key === "Escape") {
|
|
4040
|
-
callbackRefs.current.onSlashCommandClose?.();
|
|
4041
|
-
return;
|
|
4042
|
-
}
|
|
4043
|
-
if (keyEvent.key === " ") {
|
|
4044
|
-
callbackRefs.current.onSlashCommandClose?.();
|
|
4045
|
-
return;
|
|
4046
|
-
}
|
|
4047
|
-
if (keyEvent.key === "Backspace") {
|
|
4048
|
-
callbackRefs.current.onSlashCommandClose?.();
|
|
4049
|
-
return;
|
|
4050
|
-
}
|
|
4051
|
-
if (keyEvent.key !== "/") return;
|
|
4052
|
-
const selection = window.getSelection();
|
|
4053
|
-
if (!selection || !selection.rangeCount) return;
|
|
4054
|
-
const range = selection.getRangeAt(0);
|
|
4055
|
-
const textContent = target.textContent || "";
|
|
4056
|
-
const cursorOffset = range.startOffset;
|
|
4057
|
-
const charBefore = cursorOffset > 0 ? textContent[cursorOffset - 1] : "";
|
|
4058
|
-
if (cursorOffset > 0 && charBefore !== " " && charBefore !== "\xA0") {
|
|
4059
|
-
return;
|
|
4060
|
-
}
|
|
4061
|
-
const targetRect = target.getBoundingClientRect();
|
|
4062
|
-
const rangeRect = range.getBoundingClientRect();
|
|
4063
|
-
const hasValidRangeRect = rangeRect.height > 0 || (rangeRect.top !== 0 || rangeRect.left !== 0);
|
|
4064
|
-
const rect = hasValidRangeRect ? rangeRect : {
|
|
4065
|
-
top: targetRect.top,
|
|
4066
|
-
left: targetRect.left,
|
|
4067
|
-
bottom: targetRect.top + 20,
|
|
4068
|
-
right: targetRect.left,
|
|
4069
|
-
width: 0,
|
|
4070
|
-
height: 20,
|
|
4071
|
-
x: targetRect.left,
|
|
4072
|
-
y: targetRect.top,
|
|
4073
|
-
toJSON: () => ({})
|
|
4074
|
-
};
|
|
4075
|
-
callbackRefs.current.onSlashCommand?.(rect);
|
|
4076
|
-
};
|
|
4077
|
-
const handlePaste = (e) => {
|
|
4078
|
-
const clipboardEvent = e;
|
|
4079
|
-
const target = clipboardEvent.target;
|
|
4080
|
-
if (target.getAttribute("contenteditable") !== "true") return;
|
|
4081
|
-
if (!target.closest(".node-type-button")) return;
|
|
4082
|
-
const { templateSize } = useEditorStore.getState();
|
|
4083
|
-
const pastedText = clipboardEvent.clipboardData?.getData("text/plain") || "";
|
|
4084
|
-
const pasteSize = new Blob([pastedText]).size;
|
|
4085
|
-
if (templateSize + pasteSize > MAX_TEMPLATE_SIZE) {
|
|
4086
|
-
clipboardEvent.preventDefault();
|
|
4087
|
-
const now = Date.now();
|
|
4088
|
-
if (now - lastSizeLimitToastTime > 2e3) {
|
|
4089
|
-
lastSizeLimitToastTime = now;
|
|
4090
|
-
import_sonner2.toast.error("Template size limit reached");
|
|
4091
|
-
}
|
|
4092
|
-
}
|
|
4093
|
-
};
|
|
4094
|
-
shadowRoot.addEventListener("mousedown", handleMouseDown);
|
|
4095
|
-
shadowRoot.addEventListener("click", handleLinkClick, true);
|
|
4096
|
-
shadowRoot.addEventListener("mouseover", handleMouseOver);
|
|
4097
|
-
shadowRoot.addEventListener("mouseout", handleMouseOut);
|
|
4098
|
-
shadowRoot.addEventListener("input", handleInput);
|
|
4099
|
-
shadowRoot.addEventListener("focusin", handleFocus);
|
|
4100
|
-
shadowRoot.addEventListener("focusout", handleBlur);
|
|
4101
|
-
shadowRoot.addEventListener("keydown", handleKeyDown);
|
|
4102
|
-
shadowRoot.addEventListener("paste", handlePaste);
|
|
4103
|
-
shadowRoot.addEventListener("dragover", handleDragOver);
|
|
4104
|
-
shadowRoot.addEventListener("dragleave", handleDragLeave);
|
|
4105
|
-
shadowRoot.addEventListener("drop", handleDrop);
|
|
4106
|
-
return () => {
|
|
4107
|
-
if (rafRef.current) {
|
|
4108
|
-
cancelAnimationFrame(rafRef.current);
|
|
4109
|
-
rafRef.current = null;
|
|
4110
|
-
}
|
|
4111
|
-
lastBlockNodeRef.current = null;
|
|
4112
|
-
shadowRoot.removeEventListener("mousedown", handleMouseDown);
|
|
4113
|
-
shadowRoot.removeEventListener("click", handleLinkClick, true);
|
|
4114
|
-
shadowRoot.removeEventListener("mouseover", handleMouseOver);
|
|
4115
|
-
shadowRoot.removeEventListener("mouseout", handleMouseOut);
|
|
4116
|
-
shadowRoot.removeEventListener("input", handleInput);
|
|
4117
|
-
shadowRoot.removeEventListener("focusin", handleFocus);
|
|
4118
|
-
shadowRoot.removeEventListener("focusout", handleBlur);
|
|
4119
|
-
shadowRoot.removeEventListener("keydown", handleKeyDown);
|
|
4120
|
-
shadowRoot.removeEventListener("paste", handlePaste);
|
|
4121
|
-
shadowRoot.removeEventListener("dragover", handleDragOver);
|
|
4122
|
-
shadowRoot.removeEventListener("dragleave", handleDragLeave);
|
|
4123
|
-
shadowRoot.removeEventListener("drop", handleDrop);
|
|
4124
|
-
};
|
|
4125
|
-
}, []);
|
|
4126
|
-
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
|
|
4127
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
4128
|
-
"div",
|
|
4129
|
-
{
|
|
4130
|
-
ref: containerRef,
|
|
4131
|
-
className: "shadow-dom-renderer",
|
|
4132
|
-
style: {
|
|
4133
|
-
width: "100%",
|
|
4134
|
-
height: "100%",
|
|
4135
|
-
overflow: "auto"
|
|
4136
|
-
}
|
|
4137
|
-
}
|
|
4138
|
-
),
|
|
4139
|
-
dropIndicator && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
4140
|
-
"div",
|
|
4141
|
-
{
|
|
4142
|
-
className: "drop-indicator-line",
|
|
4143
|
-
style: {
|
|
4144
|
-
position: "fixed",
|
|
4145
|
-
// Horizontal indicator (existing default)
|
|
4146
|
-
...(dropIndicator.orientation === "horizontal" || !dropIndicator.orientation) && {
|
|
4147
|
-
top: dropIndicator.top - 1.5,
|
|
4148
|
-
left: dropIndicator.left,
|
|
4149
|
-
width: dropIndicator.width,
|
|
4150
|
-
height: 3
|
|
4151
|
-
},
|
|
4152
|
-
// Vertical indicator (NEW)
|
|
4153
|
-
...dropIndicator.orientation === "vertical" && {
|
|
4154
|
-
top: dropIndicator.top,
|
|
4155
|
-
left: dropIndicator.left - 1.5,
|
|
4156
|
-
width: 3,
|
|
4157
|
-
height: dropIndicator.height
|
|
4158
|
-
},
|
|
4159
|
-
backgroundColor: isDragging ? "rgb(147, 51, 234)" : "#3b82f6",
|
|
4160
|
-
borderRadius: 2,
|
|
4161
|
-
pointerEvents: "none",
|
|
4162
|
-
zIndex: 9999,
|
|
4163
|
-
boxShadow: isDragging ? "0 0 4px rgba(147, 51, 234, 0.5)" : "0 0 4px rgba(59, 130, 246, 0.5)"
|
|
4164
|
-
},
|
|
4165
|
-
children: (dropIndicator.isNewSection || dropIndicator.isSplitSection) && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
4166
|
-
"div",
|
|
4167
|
-
{
|
|
4168
|
-
style: {
|
|
4169
|
-
position: "absolute",
|
|
4170
|
-
top: "50%",
|
|
4171
|
-
left: "50%",
|
|
4172
|
-
transform: "translate(-50%, -50%)",
|
|
4173
|
-
backgroundColor: "rgb(147, 51, 234)",
|
|
4174
|
-
color: "white",
|
|
4175
|
-
fontSize: 12,
|
|
4176
|
-
fontWeight: 600,
|
|
4177
|
-
padding: "4px 10px",
|
|
4178
|
-
borderRadius: 9999,
|
|
4179
|
-
whiteSpace: "nowrap",
|
|
4180
|
-
pointerEvents: "none"
|
|
4181
|
-
},
|
|
4182
|
-
children: dropIndicator.isNewSection ? "New section" : "Split section"
|
|
4183
|
-
}
|
|
4184
|
-
)
|
|
4185
|
-
}
|
|
4186
|
-
)
|
|
4187
|
-
] });
|
|
4188
|
-
}
|
|
4189
3620
|
function getIdxFromElement(element) {
|
|
4190
3621
|
const classList = element.classList;
|
|
4191
3622
|
for (let i = 0; i < classList.length; i++) {
|
|
@@ -4243,7 +3674,7 @@ function injectContentEditable(container) {
|
|
|
4243
3674
|
}
|
|
4244
3675
|
});
|
|
4245
3676
|
}
|
|
4246
|
-
var import_react, import_sonner2, import_jsx_runtime, lastSizeLimitToastTime, cssCache;
|
|
3677
|
+
var import_react, import_sonner2, import_jsx_runtime, lastSizeLimitToastTime, cssCache, ShadowDomRenderer;
|
|
4247
3678
|
var init_ShadowDomRenderer = __esm({
|
|
4248
3679
|
"src/core/editor/components/ShadowDomRenderer.tsx"() {
|
|
4249
3680
|
"use strict";
|
|
@@ -4256,6 +3687,591 @@ var init_ShadowDomRenderer = __esm({
|
|
|
4256
3687
|
import_jsx_runtime = require("react/jsx-runtime");
|
|
4257
3688
|
lastSizeLimitToastTime = 0;
|
|
4258
3689
|
cssCache = /* @__PURE__ */ new Map();
|
|
3690
|
+
ShadowDomRenderer = (0, import_react.memo)(function ShadowDomRenderer2({
|
|
3691
|
+
html,
|
|
3692
|
+
focusIdx,
|
|
3693
|
+
hoverIdx,
|
|
3694
|
+
dropIndicator,
|
|
3695
|
+
dropTargetIdx,
|
|
3696
|
+
isDragging = false,
|
|
3697
|
+
isDragButtonHovered = false,
|
|
3698
|
+
textEditingIdx = null,
|
|
3699
|
+
showCompanyFooter = true,
|
|
3700
|
+
onElementClick,
|
|
3701
|
+
onElementHover,
|
|
3702
|
+
onContentInput,
|
|
3703
|
+
onEditingStart,
|
|
3704
|
+
onEditingEnd,
|
|
3705
|
+
onDragOver,
|
|
3706
|
+
onDrop,
|
|
3707
|
+
onDragLeave,
|
|
3708
|
+
onSelectionRectChange,
|
|
3709
|
+
onShadowRootRef,
|
|
3710
|
+
onSlashCommand,
|
|
3711
|
+
onSlashCommandClose,
|
|
3712
|
+
onTextEditStart
|
|
3713
|
+
}) {
|
|
3714
|
+
const containerRef = (0, import_react.useRef)(null);
|
|
3715
|
+
const shadowRootRef = (0, import_react.useRef)(null);
|
|
3716
|
+
const rafRef = (0, import_react.useRef)(null);
|
|
3717
|
+
const lastBlockNodeRef = (0, import_react.useRef)(null);
|
|
3718
|
+
const hoverRafRef = (0, import_react.useRef)(null);
|
|
3719
|
+
const lastHoverIdxRef = (0, import_react.useRef)(null);
|
|
3720
|
+
const callbackRefs = (0, import_react.useRef)({
|
|
3721
|
+
onElementClick,
|
|
3722
|
+
onElementHover,
|
|
3723
|
+
onContentInput,
|
|
3724
|
+
onEditingStart,
|
|
3725
|
+
onEditingEnd,
|
|
3726
|
+
onDragOver,
|
|
3727
|
+
onDrop,
|
|
3728
|
+
onDragLeave,
|
|
3729
|
+
onSlashCommand,
|
|
3730
|
+
onSlashCommandClose,
|
|
3731
|
+
onTextEditStart
|
|
3732
|
+
});
|
|
3733
|
+
(0, import_react.useEffect)(() => {
|
|
3734
|
+
callbackRefs.current = {
|
|
3735
|
+
onElementClick,
|
|
3736
|
+
onElementHover,
|
|
3737
|
+
onContentInput,
|
|
3738
|
+
onEditingStart,
|
|
3739
|
+
onEditingEnd,
|
|
3740
|
+
onDragOver,
|
|
3741
|
+
onDrop,
|
|
3742
|
+
onDragLeave,
|
|
3743
|
+
onSlashCommand,
|
|
3744
|
+
onSlashCommandClose,
|
|
3745
|
+
onTextEditStart
|
|
3746
|
+
};
|
|
3747
|
+
});
|
|
3748
|
+
(0, import_react.useEffect)(() => {
|
|
3749
|
+
if (containerRef.current && !shadowRootRef.current) {
|
|
3750
|
+
shadowRootRef.current = containerRef.current.attachShadow({ mode: "open" });
|
|
3751
|
+
onShadowRootRef?.(shadowRootRef.current);
|
|
3752
|
+
}
|
|
3753
|
+
}, [onShadowRootRef]);
|
|
3754
|
+
(0, import_react.useEffect)(() => {
|
|
3755
|
+
if (!shadowRootRef.current) return;
|
|
3756
|
+
const styleEl = document.createElement("style");
|
|
3757
|
+
styleEl.id = "editor-styles";
|
|
3758
|
+
styleEl.textContent = getEditorStyles(isDragButtonHovered, textEditingIdx);
|
|
3759
|
+
const contentWrapper = document.createElement("div");
|
|
3760
|
+
contentWrapper.className = "shadow-content";
|
|
3761
|
+
contentWrapper.innerHTML = html;
|
|
3762
|
+
contentWrapper.style.setProperty("--company-footer-opacity", showCompanyFooter ? "1" : "0.25");
|
|
3763
|
+
contentWrapper.style.setProperty("--outline-color", isDragging ? "rgb(147, 51, 234)" : "rgb(59, 130, 246)");
|
|
3764
|
+
contentWrapper.style.setProperty("--outline-color-hover", isDragging ? "rgba(147, 51, 234, 0.5)" : "rgba(59, 130, 246, 0.5)");
|
|
3765
|
+
const template = useEditorStore.getState().template;
|
|
3766
|
+
const linkColor2 = template?.content?.[0]?.data?.value?.linkColor || "#0000ff";
|
|
3767
|
+
contentWrapper.style.setProperty("--link-color", linkColor2);
|
|
3768
|
+
injectContentEditable(contentWrapper);
|
|
3769
|
+
shadowRootRef.current.innerHTML = "";
|
|
3770
|
+
shadowRootRef.current.appendChild(styleEl);
|
|
3771
|
+
shadowRootRef.current.appendChild(contentWrapper);
|
|
3772
|
+
}, [html]);
|
|
3773
|
+
(0, import_react.useEffect)(() => {
|
|
3774
|
+
if (!shadowRootRef.current) return;
|
|
3775
|
+
const styleEl = shadowRootRef.current.querySelector("#editor-styles");
|
|
3776
|
+
if (styleEl) {
|
|
3777
|
+
styleEl.textContent = getEditorStyles(isDragButtonHovered, textEditingIdx);
|
|
3778
|
+
}
|
|
3779
|
+
}, [isDragButtonHovered, textEditingIdx]);
|
|
3780
|
+
(0, import_react.useEffect)(() => {
|
|
3781
|
+
if (!shadowRootRef.current) return;
|
|
3782
|
+
const contentWrapper = shadowRootRef.current.querySelector(".shadow-content");
|
|
3783
|
+
if (contentWrapper) {
|
|
3784
|
+
contentWrapper.style.setProperty("--outline-color", isDragging ? "rgb(147, 51, 234)" : "rgb(59, 130, 246)");
|
|
3785
|
+
contentWrapper.style.setProperty("--outline-color-hover", isDragging ? "rgba(147, 51, 234, 0.5)" : "rgba(59, 130, 246, 0.5)");
|
|
3786
|
+
contentWrapper.style.setProperty("--company-footer-opacity", showCompanyFooter ? "1" : "0.25");
|
|
3787
|
+
const template = useEditorStore.getState().template;
|
|
3788
|
+
const linkColor2 = template?.content?.[0]?.data?.value?.linkColor || "#0000ff";
|
|
3789
|
+
contentWrapper.style.setProperty("--link-color", linkColor2);
|
|
3790
|
+
}
|
|
3791
|
+
}, [isDragging, showCompanyFooter]);
|
|
3792
|
+
const linkColor = useEditorStore((state) => state.template?.content?.[0]?.data?.value?.linkColor);
|
|
3793
|
+
(0, import_react.useEffect)(() => {
|
|
3794
|
+
if (!shadowRootRef.current) return;
|
|
3795
|
+
const contentWrapper = shadowRootRef.current.querySelector(".shadow-content");
|
|
3796
|
+
if (contentWrapper) {
|
|
3797
|
+
contentWrapper.style.setProperty("--link-color", linkColor || "#0000ff");
|
|
3798
|
+
}
|
|
3799
|
+
}, [linkColor]);
|
|
3800
|
+
(0, import_react.useEffect)(() => {
|
|
3801
|
+
if (!shadowRootRef.current) return;
|
|
3802
|
+
shadowRootRef.current.querySelectorAll(".is-selected").forEach((el) => {
|
|
3803
|
+
el.classList.remove("is-selected");
|
|
3804
|
+
});
|
|
3805
|
+
shadowRootRef.current.querySelectorAll(".is-column-selected").forEach((el) => {
|
|
3806
|
+
el.classList.remove("is-column-selected");
|
|
3807
|
+
});
|
|
3808
|
+
shadowRootRef.current.querySelectorAll(".is-card-selected").forEach((el) => {
|
|
3809
|
+
el.classList.remove("is-card-selected");
|
|
3810
|
+
});
|
|
3811
|
+
if (focusIdx) {
|
|
3812
|
+
const selectedEl = shadowRootRef.current.querySelector(
|
|
3813
|
+
`.node-idx-${CSS.escape(focusIdx)}`
|
|
3814
|
+
);
|
|
3815
|
+
if (selectedEl) {
|
|
3816
|
+
if (focusIdx !== "content") {
|
|
3817
|
+
selectedEl.classList.add("is-selected");
|
|
3818
|
+
}
|
|
3819
|
+
if (selectedEl.classList.contains("node-type-section-column")) {
|
|
3820
|
+
selectedEl.querySelectorAll(".node-type-column").forEach((col) => {
|
|
3821
|
+
col.classList.add("is-column-selected");
|
|
3822
|
+
});
|
|
3823
|
+
} else if (selectedEl.classList.contains("node-type-column")) {
|
|
3824
|
+
const parentSection = selectedEl.closest(".node-type-section-column");
|
|
3825
|
+
if (parentSection) {
|
|
3826
|
+
parentSection.querySelectorAll(".node-type-column").forEach((col) => {
|
|
3827
|
+
col.classList.add("is-column-selected");
|
|
3828
|
+
});
|
|
3829
|
+
}
|
|
3830
|
+
} else {
|
|
3831
|
+
const parentSectionColumn = selectedEl.closest(".node-type-section-column");
|
|
3832
|
+
if (parentSectionColumn) {
|
|
3833
|
+
const parentColumn = selectedEl.closest(".node-type-column");
|
|
3834
|
+
if (parentColumn) {
|
|
3835
|
+
parentColumn.classList.add("is-column-selected");
|
|
3836
|
+
}
|
|
3837
|
+
}
|
|
3838
|
+
}
|
|
3839
|
+
if (selectedEl.classList.contains("node-type-section-property-triple")) {
|
|
3840
|
+
selectedEl.querySelectorAll(".node-type-property-card-triple-item").forEach((card) => {
|
|
3841
|
+
card.classList.add("is-card-selected");
|
|
3842
|
+
});
|
|
3843
|
+
} else if (selectedEl.classList.contains("node-type-property-card-triple")) {
|
|
3844
|
+
selectedEl.querySelectorAll(".node-type-property-card-triple-item").forEach((card) => {
|
|
3845
|
+
card.classList.add("is-card-selected");
|
|
3846
|
+
});
|
|
3847
|
+
} else if (selectedEl.classList.contains("node-type-property-card-triple-item")) {
|
|
3848
|
+
selectedEl.classList.add("is-card-selected");
|
|
3849
|
+
}
|
|
3850
|
+
onSelectionRectChange?.(selectedEl.getBoundingClientRect());
|
|
3851
|
+
} else {
|
|
3852
|
+
onSelectionRectChange?.(null);
|
|
3853
|
+
}
|
|
3854
|
+
} else {
|
|
3855
|
+
onSelectionRectChange?.(null);
|
|
3856
|
+
}
|
|
3857
|
+
}, [focusIdx, html, onSelectionRectChange]);
|
|
3858
|
+
(0, import_react.useEffect)(() => {
|
|
3859
|
+
if (!shadowRootRef.current) return;
|
|
3860
|
+
shadowRootRef.current.querySelectorAll(".is-hovered").forEach((el) => {
|
|
3861
|
+
el.classList.remove("is-hovered");
|
|
3862
|
+
});
|
|
3863
|
+
shadowRootRef.current.querySelectorAll(".is-column-hover-parent").forEach((el) => {
|
|
3864
|
+
el.classList.remove("is-column-hover-parent");
|
|
3865
|
+
});
|
|
3866
|
+
if (hoverIdx && hoverIdx !== focusIdx && hoverIdx !== "content") {
|
|
3867
|
+
const hoveredEl = shadowRootRef.current.querySelector(
|
|
3868
|
+
`.node-idx-${CSS.escape(hoverIdx)}`
|
|
3869
|
+
);
|
|
3870
|
+
if (hoveredEl) {
|
|
3871
|
+
hoveredEl.classList.add("is-hovered");
|
|
3872
|
+
if (!hoveredEl.classList.contains("node-type-column")) {
|
|
3873
|
+
const parentSectionColumn = hoveredEl.closest(".node-type-section-column");
|
|
3874
|
+
if (parentSectionColumn) {
|
|
3875
|
+
const parentColumn = hoveredEl.closest(".node-type-column");
|
|
3876
|
+
if (parentColumn) {
|
|
3877
|
+
parentColumn.classList.add("is-column-hover-parent");
|
|
3878
|
+
}
|
|
3879
|
+
}
|
|
3880
|
+
}
|
|
3881
|
+
}
|
|
3882
|
+
}
|
|
3883
|
+
}, [focusIdx, hoverIdx, html]);
|
|
3884
|
+
(0, import_react.useEffect)(() => {
|
|
3885
|
+
if (!shadowRootRef.current) return;
|
|
3886
|
+
shadowRootRef.current.querySelectorAll(".is-drop-target").forEach((el) => {
|
|
3887
|
+
el.classList.remove("is-drop-target");
|
|
3888
|
+
});
|
|
3889
|
+
if (dropTargetIdx && isDragging) {
|
|
3890
|
+
const targetEl = shadowRootRef.current.querySelector(
|
|
3891
|
+
`.node-idx-${CSS.escape(dropTargetIdx)}`
|
|
3892
|
+
);
|
|
3893
|
+
if (targetEl) {
|
|
3894
|
+
targetEl.classList.add("is-drop-target");
|
|
3895
|
+
}
|
|
3896
|
+
}
|
|
3897
|
+
}, [dropTargetIdx, isDragging]);
|
|
3898
|
+
(0, import_react.useEffect)(() => {
|
|
3899
|
+
const shadowRoot = shadowRootRef.current;
|
|
3900
|
+
if (!shadowRoot) return;
|
|
3901
|
+
const handleMouseDown = (e) => {
|
|
3902
|
+
const target = e.target;
|
|
3903
|
+
const mouseEvent = e;
|
|
3904
|
+
if (target.closest('[contenteditable="true"]')) {
|
|
3905
|
+
return;
|
|
3906
|
+
}
|
|
3907
|
+
const blockNode = target.closest(`.${EMAIL_BLOCK_CLASS_NAME}`);
|
|
3908
|
+
if (blockNode && callbackRefs.current.onElementClick) {
|
|
3909
|
+
const idx = getIdxFromElement(blockNode);
|
|
3910
|
+
if (idx) {
|
|
3911
|
+
callbackRefs.current.onElementClick(idx);
|
|
3912
|
+
if (blockNode.classList.contains("node-type-text") && callbackRefs.current.onTextEditStart) {
|
|
3913
|
+
const contentDiv = blockNode.querySelector("div");
|
|
3914
|
+
if (contentDiv) {
|
|
3915
|
+
const blockEl = blockNode;
|
|
3916
|
+
const blockRect = blockEl.getBoundingClientRect();
|
|
3917
|
+
const blockStyle = window.getComputedStyle(blockEl);
|
|
3918
|
+
const content = contentDiv.innerHTML;
|
|
3919
|
+
const clickX = mouseEvent.clientX;
|
|
3920
|
+
const clickY = mouseEvent.clientY;
|
|
3921
|
+
const contentStyle = window.getComputedStyle(contentDiv);
|
|
3922
|
+
const styles = {
|
|
3923
|
+
fontFamily: contentStyle.fontFamily,
|
|
3924
|
+
fontSize: contentStyle.fontSize,
|
|
3925
|
+
fontWeight: contentStyle.fontWeight,
|
|
3926
|
+
lineHeight: contentStyle.lineHeight,
|
|
3927
|
+
color: contentStyle.color,
|
|
3928
|
+
textAlign: contentStyle.textAlign,
|
|
3929
|
+
padding: blockStyle.padding,
|
|
3930
|
+
// Use td's padding
|
|
3931
|
+
// Additional text-rendering properties for exact matching
|
|
3932
|
+
letterSpacing: contentStyle.letterSpacing,
|
|
3933
|
+
wordSpacing: contentStyle.wordSpacing,
|
|
3934
|
+
textRendering: contentStyle.textRendering,
|
|
3935
|
+
fontStyle: contentStyle.fontStyle,
|
|
3936
|
+
textDecoration: contentStyle.textDecoration,
|
|
3937
|
+
textTransform: contentStyle.textTransform,
|
|
3938
|
+
fontVariant: contentStyle.fontVariant,
|
|
3939
|
+
textIndent: contentStyle.textIndent
|
|
3940
|
+
};
|
|
3941
|
+
callbackRefs.current.onTextEditStart?.(idx, blockRect.width, blockRect.height, clickX, clickY, content, styles);
|
|
3942
|
+
}
|
|
3943
|
+
}
|
|
3944
|
+
}
|
|
3945
|
+
}
|
|
3946
|
+
};
|
|
3947
|
+
const handleMouseOver = (e) => {
|
|
3948
|
+
const target = e.target;
|
|
3949
|
+
const blockNode = target.closest(`.${EMAIL_BLOCK_CLASS_NAME}`);
|
|
3950
|
+
const newIdx = blockNode ? getIdxFromElement(blockNode) : null;
|
|
3951
|
+
if (newIdx === lastHoverIdxRef.current) return;
|
|
3952
|
+
if (hoverRafRef.current) return;
|
|
3953
|
+
hoverRafRef.current = requestAnimationFrame(() => {
|
|
3954
|
+
hoverRafRef.current = null;
|
|
3955
|
+
lastHoverIdxRef.current = newIdx;
|
|
3956
|
+
callbackRefs.current.onElementHover?.(newIdx);
|
|
3957
|
+
});
|
|
3958
|
+
};
|
|
3959
|
+
const handleMouseOut = (e) => {
|
|
3960
|
+
const relatedTarget = e.relatedTarget;
|
|
3961
|
+
if (!relatedTarget || !shadowRoot.contains(relatedTarget)) {
|
|
3962
|
+
lastHoverIdxRef.current = null;
|
|
3963
|
+
callbackRefs.current.onElementHover?.(null);
|
|
3964
|
+
}
|
|
3965
|
+
};
|
|
3966
|
+
const handleInput = (e) => {
|
|
3967
|
+
const target = e.target;
|
|
3968
|
+
if (target.getAttribute("contenteditable") === "true" && callbackRefs.current.onContentInput) {
|
|
3969
|
+
const contentIdx = target.getAttribute("data-content-idx");
|
|
3970
|
+
if (contentIdx) {
|
|
3971
|
+
const isRichText = target.closest(".node-type-text");
|
|
3972
|
+
const content = isRichText ? target.innerHTML : target.textContent || "";
|
|
3973
|
+
callbackRefs.current.onContentInput(contentIdx, content);
|
|
3974
|
+
if (isRichText) {
|
|
3975
|
+
if (isTextContentEmpty(target)) {
|
|
3976
|
+
target.classList.add("is-empty");
|
|
3977
|
+
} else {
|
|
3978
|
+
target.classList.remove("is-empty");
|
|
3979
|
+
}
|
|
3980
|
+
}
|
|
3981
|
+
}
|
|
3982
|
+
}
|
|
3983
|
+
};
|
|
3984
|
+
const handleFocus = (e) => {
|
|
3985
|
+
const target = e.target;
|
|
3986
|
+
if (target.getAttribute("contenteditable") === "true") {
|
|
3987
|
+
callbackRefs.current.onEditingStart?.();
|
|
3988
|
+
const blockNode = target.closest(`.${EMAIL_BLOCK_CLASS_NAME}`);
|
|
3989
|
+
if (blockNode && callbackRefs.current.onElementClick) {
|
|
3990
|
+
const idx = getIdxFromElement(blockNode);
|
|
3991
|
+
if (idx) {
|
|
3992
|
+
callbackRefs.current.onElementClick(idx);
|
|
3993
|
+
}
|
|
3994
|
+
}
|
|
3995
|
+
}
|
|
3996
|
+
};
|
|
3997
|
+
const handleBlur = (e) => {
|
|
3998
|
+
const target = e.target;
|
|
3999
|
+
if (target.getAttribute("contenteditable") === "true") {
|
|
4000
|
+
callbackRefs.current.onEditingEnd?.();
|
|
4001
|
+
}
|
|
4002
|
+
};
|
|
4003
|
+
const getParagraphInfo = (blockNode, mouseY) => {
|
|
4004
|
+
if (!blockNode.classList.contains("node-type-text")) return void 0;
|
|
4005
|
+
const contentDiv = blockNode.querySelector("div");
|
|
4006
|
+
if (!contentDiv) return void 0;
|
|
4007
|
+
const paragraphs = Array.from(contentDiv.querySelectorAll("p"));
|
|
4008
|
+
if (paragraphs.length < 2) return void 0;
|
|
4009
|
+
for (let i = 0; i < paragraphs.length; i++) {
|
|
4010
|
+
const pRect = paragraphs[i].getBoundingClientRect();
|
|
4011
|
+
if (mouseY >= pRect.top && mouseY <= pRect.bottom) {
|
|
4012
|
+
const midpoint = pRect.top + pRect.height / 2;
|
|
4013
|
+
return {
|
|
4014
|
+
paragraphIndex: i,
|
|
4015
|
+
totalParagraphs: paragraphs.length,
|
|
4016
|
+
paragraphRect: pRect,
|
|
4017
|
+
isAboveMidpoint: mouseY < midpoint
|
|
4018
|
+
};
|
|
4019
|
+
}
|
|
4020
|
+
}
|
|
4021
|
+
return void 0;
|
|
4022
|
+
};
|
|
4023
|
+
const handleDragOver = (e) => {
|
|
4024
|
+
const dragEvent = e;
|
|
4025
|
+
dragEvent.preventDefault();
|
|
4026
|
+
const target = dragEvent.target;
|
|
4027
|
+
const mouseY = dragEvent.clientY;
|
|
4028
|
+
const mouseX = dragEvent.clientX;
|
|
4029
|
+
let blockNode = target.closest(`.${EMAIL_BLOCK_CLASS_NAME}`);
|
|
4030
|
+
if (blockNode && getTypeFromElement(blockNode) === "social-item") {
|
|
4031
|
+
blockNode = blockNode.parentElement?.closest(`.${EMAIL_BLOCK_CLASS_NAME}`) || null;
|
|
4032
|
+
}
|
|
4033
|
+
if (rafRef.current) return;
|
|
4034
|
+
rafRef.current = requestAnimationFrame(() => {
|
|
4035
|
+
rafRef.current = null;
|
|
4036
|
+
if (blockNode === lastBlockNodeRef.current && blockNode) {
|
|
4037
|
+
const dragInfo2 = {
|
|
4038
|
+
idx: getIdxFromElement(blockNode),
|
|
4039
|
+
type: getTypeFromElement(blockNode),
|
|
4040
|
+
rect: blockNode.getBoundingClientRect(),
|
|
4041
|
+
mouseY,
|
|
4042
|
+
// Use captured value
|
|
4043
|
+
mouseX,
|
|
4044
|
+
// Use captured value
|
|
4045
|
+
paragraphInfo: getParagraphInfo(blockNode, mouseY)
|
|
4046
|
+
};
|
|
4047
|
+
callbackRefs.current.onDragOver?.(dragEvent, dragInfo2);
|
|
4048
|
+
return;
|
|
4049
|
+
}
|
|
4050
|
+
lastBlockNodeRef.current = blockNode;
|
|
4051
|
+
const dragInfo = {
|
|
4052
|
+
idx: blockNode ? getIdxFromElement(blockNode) : null,
|
|
4053
|
+
type: blockNode ? getTypeFromElement(blockNode) : null,
|
|
4054
|
+
rect: blockNode ? blockNode.getBoundingClientRect() : null,
|
|
4055
|
+
mouseY,
|
|
4056
|
+
// Use captured value
|
|
4057
|
+
mouseX,
|
|
4058
|
+
// Use captured value
|
|
4059
|
+
paragraphInfo: blockNode ? getParagraphInfo(blockNode, mouseY) : void 0
|
|
4060
|
+
};
|
|
4061
|
+
callbackRefs.current.onDragOver?.(dragEvent, dragInfo);
|
|
4062
|
+
});
|
|
4063
|
+
};
|
|
4064
|
+
const handleDragLeave = (e) => {
|
|
4065
|
+
const dragEvent = e;
|
|
4066
|
+
const relatedTarget = dragEvent.relatedTarget;
|
|
4067
|
+
if (!relatedTarget || !shadowRoot.contains(relatedTarget)) {
|
|
4068
|
+
lastBlockNodeRef.current = null;
|
|
4069
|
+
callbackRefs.current.onDragLeave?.();
|
|
4070
|
+
}
|
|
4071
|
+
};
|
|
4072
|
+
const handleDrop = (e) => {
|
|
4073
|
+
const dragEvent = e;
|
|
4074
|
+
dragEvent.preventDefault();
|
|
4075
|
+
const target = dragEvent.target;
|
|
4076
|
+
const mouseY = dragEvent.clientY;
|
|
4077
|
+
const mouseX = dragEvent.clientX;
|
|
4078
|
+
const blockNode = target.closest(`.${EMAIL_BLOCK_CLASS_NAME}`);
|
|
4079
|
+
const dragInfo = {
|
|
4080
|
+
idx: blockNode ? getIdxFromElement(blockNode) : null,
|
|
4081
|
+
type: blockNode ? getTypeFromElement(blockNode) : null,
|
|
4082
|
+
rect: blockNode ? blockNode.getBoundingClientRect() : null,
|
|
4083
|
+
mouseY,
|
|
4084
|
+
mouseX,
|
|
4085
|
+
paragraphInfo: blockNode ? getParagraphInfo(blockNode, mouseY) : void 0
|
|
4086
|
+
};
|
|
4087
|
+
callbackRefs.current.onDrop?.(dragEvent, dragInfo);
|
|
4088
|
+
};
|
|
4089
|
+
const handleLinkClick = (e) => {
|
|
4090
|
+
const target = e.target;
|
|
4091
|
+
if (target.tagName === "A" || target.closest("a")) {
|
|
4092
|
+
e.preventDefault();
|
|
4093
|
+
}
|
|
4094
|
+
};
|
|
4095
|
+
const handleKeyDown = (e) => {
|
|
4096
|
+
const keyEvent = e;
|
|
4097
|
+
const target = keyEvent.target;
|
|
4098
|
+
if (target.getAttribute("contenteditable") === "true" && target.closest(".node-type-button")) {
|
|
4099
|
+
const isAtSizeLimit = useEditorStore.getState().isAtSizeLimit;
|
|
4100
|
+
if (isAtSizeLimit) {
|
|
4101
|
+
const allowedKeys = ["Backspace", "Delete", "ArrowLeft", "ArrowRight", "ArrowUp", "ArrowDown", "Escape", "Tab"];
|
|
4102
|
+
const isModifier = keyEvent.ctrlKey || keyEvent.metaKey || keyEvent.altKey;
|
|
4103
|
+
const isAllowed = allowedKeys.includes(keyEvent.key) || isModifier;
|
|
4104
|
+
if (!isAllowed && (keyEvent.key.length === 1 || keyEvent.key === "Enter")) {
|
|
4105
|
+
keyEvent.preventDefault();
|
|
4106
|
+
const now = Date.now();
|
|
4107
|
+
if (now - lastSizeLimitToastTime > 2e3) {
|
|
4108
|
+
lastSizeLimitToastTime = now;
|
|
4109
|
+
import_sonner2.toast.error("Template size limit reached");
|
|
4110
|
+
}
|
|
4111
|
+
return;
|
|
4112
|
+
}
|
|
4113
|
+
}
|
|
4114
|
+
return;
|
|
4115
|
+
}
|
|
4116
|
+
const relevantKeys = ["/", "Escape", " ", "Backspace"];
|
|
4117
|
+
if (!relevantKeys.includes(keyEvent.key)) return;
|
|
4118
|
+
if (target.getAttribute("contenteditable") !== "true") return;
|
|
4119
|
+
if (!target.closest(".node-type-text")) return;
|
|
4120
|
+
if (keyEvent.key === "Escape") {
|
|
4121
|
+
callbackRefs.current.onSlashCommandClose?.();
|
|
4122
|
+
return;
|
|
4123
|
+
}
|
|
4124
|
+
if (keyEvent.key === " ") {
|
|
4125
|
+
callbackRefs.current.onSlashCommandClose?.();
|
|
4126
|
+
return;
|
|
4127
|
+
}
|
|
4128
|
+
if (keyEvent.key === "Backspace") {
|
|
4129
|
+
callbackRefs.current.onSlashCommandClose?.();
|
|
4130
|
+
return;
|
|
4131
|
+
}
|
|
4132
|
+
if (keyEvent.key !== "/") return;
|
|
4133
|
+
const selection = window.getSelection();
|
|
4134
|
+
if (!selection || !selection.rangeCount) return;
|
|
4135
|
+
const range = selection.getRangeAt(0);
|
|
4136
|
+
const textContent = target.textContent || "";
|
|
4137
|
+
const cursorOffset = range.startOffset;
|
|
4138
|
+
const charBefore = cursorOffset > 0 ? textContent[cursorOffset - 1] : "";
|
|
4139
|
+
if (cursorOffset > 0 && charBefore !== " " && charBefore !== "\xA0") {
|
|
4140
|
+
return;
|
|
4141
|
+
}
|
|
4142
|
+
const targetRect = target.getBoundingClientRect();
|
|
4143
|
+
const rangeRect = range.getBoundingClientRect();
|
|
4144
|
+
const hasValidRangeRect = rangeRect.height > 0 || (rangeRect.top !== 0 || rangeRect.left !== 0);
|
|
4145
|
+
const rect = hasValidRangeRect ? rangeRect : {
|
|
4146
|
+
top: targetRect.top,
|
|
4147
|
+
left: targetRect.left,
|
|
4148
|
+
bottom: targetRect.top + 20,
|
|
4149
|
+
right: targetRect.left,
|
|
4150
|
+
width: 0,
|
|
4151
|
+
height: 20,
|
|
4152
|
+
x: targetRect.left,
|
|
4153
|
+
y: targetRect.top,
|
|
4154
|
+
toJSON: () => ({})
|
|
4155
|
+
};
|
|
4156
|
+
callbackRefs.current.onSlashCommand?.(rect);
|
|
4157
|
+
};
|
|
4158
|
+
const handlePaste = (e) => {
|
|
4159
|
+
const clipboardEvent = e;
|
|
4160
|
+
const target = clipboardEvent.target;
|
|
4161
|
+
if (target.getAttribute("contenteditable") !== "true") return;
|
|
4162
|
+
if (!target.closest(".node-type-button")) return;
|
|
4163
|
+
const { templateSize } = useEditorStore.getState();
|
|
4164
|
+
const pastedText = clipboardEvent.clipboardData?.getData("text/plain") || "";
|
|
4165
|
+
const pasteSize = new Blob([pastedText]).size;
|
|
4166
|
+
if (templateSize + pasteSize > MAX_TEMPLATE_SIZE) {
|
|
4167
|
+
clipboardEvent.preventDefault();
|
|
4168
|
+
const now = Date.now();
|
|
4169
|
+
if (now - lastSizeLimitToastTime > 2e3) {
|
|
4170
|
+
lastSizeLimitToastTime = now;
|
|
4171
|
+
import_sonner2.toast.error("Template size limit reached");
|
|
4172
|
+
}
|
|
4173
|
+
}
|
|
4174
|
+
};
|
|
4175
|
+
shadowRoot.addEventListener("mousedown", handleMouseDown);
|
|
4176
|
+
shadowRoot.addEventListener("click", handleLinkClick, true);
|
|
4177
|
+
shadowRoot.addEventListener("mouseover", handleMouseOver);
|
|
4178
|
+
shadowRoot.addEventListener("mouseout", handleMouseOut);
|
|
4179
|
+
shadowRoot.addEventListener("input", handleInput);
|
|
4180
|
+
shadowRoot.addEventListener("focusin", handleFocus);
|
|
4181
|
+
shadowRoot.addEventListener("focusout", handleBlur);
|
|
4182
|
+
shadowRoot.addEventListener("keydown", handleKeyDown);
|
|
4183
|
+
shadowRoot.addEventListener("paste", handlePaste);
|
|
4184
|
+
shadowRoot.addEventListener("dragover", handleDragOver);
|
|
4185
|
+
shadowRoot.addEventListener("dragleave", handleDragLeave);
|
|
4186
|
+
shadowRoot.addEventListener("drop", handleDrop);
|
|
4187
|
+
return () => {
|
|
4188
|
+
if (rafRef.current) {
|
|
4189
|
+
cancelAnimationFrame(rafRef.current);
|
|
4190
|
+
rafRef.current = null;
|
|
4191
|
+
}
|
|
4192
|
+
lastBlockNodeRef.current = null;
|
|
4193
|
+
if (hoverRafRef.current) {
|
|
4194
|
+
cancelAnimationFrame(hoverRafRef.current);
|
|
4195
|
+
hoverRafRef.current = null;
|
|
4196
|
+
}
|
|
4197
|
+
lastHoverIdxRef.current = null;
|
|
4198
|
+
shadowRoot.removeEventListener("mousedown", handleMouseDown);
|
|
4199
|
+
shadowRoot.removeEventListener("click", handleLinkClick, true);
|
|
4200
|
+
shadowRoot.removeEventListener("mouseover", handleMouseOver);
|
|
4201
|
+
shadowRoot.removeEventListener("mouseout", handleMouseOut);
|
|
4202
|
+
shadowRoot.removeEventListener("input", handleInput);
|
|
4203
|
+
shadowRoot.removeEventListener("focusin", handleFocus);
|
|
4204
|
+
shadowRoot.removeEventListener("focusout", handleBlur);
|
|
4205
|
+
shadowRoot.removeEventListener("keydown", handleKeyDown);
|
|
4206
|
+
shadowRoot.removeEventListener("paste", handlePaste);
|
|
4207
|
+
shadowRoot.removeEventListener("dragover", handleDragOver);
|
|
4208
|
+
shadowRoot.removeEventListener("dragleave", handleDragLeave);
|
|
4209
|
+
shadowRoot.removeEventListener("drop", handleDrop);
|
|
4210
|
+
};
|
|
4211
|
+
}, []);
|
|
4212
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
|
|
4213
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
4214
|
+
"div",
|
|
4215
|
+
{
|
|
4216
|
+
ref: containerRef,
|
|
4217
|
+
className: "shadow-dom-renderer",
|
|
4218
|
+
style: {
|
|
4219
|
+
width: "100%",
|
|
4220
|
+
height: "100%",
|
|
4221
|
+
overflow: "auto"
|
|
4222
|
+
}
|
|
4223
|
+
}
|
|
4224
|
+
),
|
|
4225
|
+
dropIndicator && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
4226
|
+
"div",
|
|
4227
|
+
{
|
|
4228
|
+
className: "drop-indicator-line",
|
|
4229
|
+
style: {
|
|
4230
|
+
position: "fixed",
|
|
4231
|
+
// Horizontal indicator (existing default)
|
|
4232
|
+
...(dropIndicator.orientation === "horizontal" || !dropIndicator.orientation) && {
|
|
4233
|
+
top: dropIndicator.top - 1.5,
|
|
4234
|
+
left: dropIndicator.left,
|
|
4235
|
+
width: dropIndicator.width,
|
|
4236
|
+
height: 3
|
|
4237
|
+
},
|
|
4238
|
+
// Vertical indicator (NEW)
|
|
4239
|
+
...dropIndicator.orientation === "vertical" && {
|
|
4240
|
+
top: dropIndicator.top,
|
|
4241
|
+
left: dropIndicator.left - 1.5,
|
|
4242
|
+
width: 3,
|
|
4243
|
+
height: dropIndicator.height
|
|
4244
|
+
},
|
|
4245
|
+
backgroundColor: isDragging ? "rgb(147, 51, 234)" : "#3b82f6",
|
|
4246
|
+
borderRadius: 2,
|
|
4247
|
+
pointerEvents: "none",
|
|
4248
|
+
zIndex: 9999,
|
|
4249
|
+
boxShadow: isDragging ? "0 0 4px rgba(147, 51, 234, 0.5)" : "0 0 4px rgba(59, 130, 246, 0.5)"
|
|
4250
|
+
},
|
|
4251
|
+
children: (dropIndicator.isNewSection || dropIndicator.isSplitSection) && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
4252
|
+
"div",
|
|
4253
|
+
{
|
|
4254
|
+
style: {
|
|
4255
|
+
position: "absolute",
|
|
4256
|
+
top: "50%",
|
|
4257
|
+
left: "50%",
|
|
4258
|
+
transform: "translate(-50%, -50%)",
|
|
4259
|
+
backgroundColor: "rgb(147, 51, 234)",
|
|
4260
|
+
color: "white",
|
|
4261
|
+
fontSize: 12,
|
|
4262
|
+
fontWeight: 600,
|
|
4263
|
+
padding: "4px 10px",
|
|
4264
|
+
borderRadius: 9999,
|
|
4265
|
+
whiteSpace: "nowrap",
|
|
4266
|
+
pointerEvents: "none"
|
|
4267
|
+
},
|
|
4268
|
+
children: dropIndicator.isNewSection ? "New section" : "Split section"
|
|
4269
|
+
}
|
|
4270
|
+
)
|
|
4271
|
+
}
|
|
4272
|
+
)
|
|
4273
|
+
] });
|
|
4274
|
+
});
|
|
4259
4275
|
}
|
|
4260
4276
|
});
|
|
4261
4277
|
|
|
@@ -12641,7 +12657,7 @@ var init_use_href = __esm({
|
|
|
12641
12657
|
}
|
|
12642
12658
|
}
|
|
12643
12659
|
}, [href]);
|
|
12644
|
-
const hasHref = Boolean(href && href !== "
|
|
12660
|
+
const hasHref = Boolean(href && href !== "" && href.trim() !== "");
|
|
12645
12661
|
return {
|
|
12646
12662
|
href,
|
|
12647
12663
|
setHref,
|
|
@@ -14633,23 +14649,9 @@ var init_useMjmlCompiler = __esm({
|
|
|
14633
14649
|
}
|
|
14634
14650
|
});
|
|
14635
14651
|
|
|
14636
|
-
// src/core/editor/hooks/use-user.ts
|
|
14637
|
-
function useUser() {
|
|
14638
|
-
return {
|
|
14639
|
-
isPaid: true
|
|
14640
|
-
// toggle to true for testing paid features
|
|
14641
|
-
};
|
|
14642
|
-
}
|
|
14643
|
-
var init_use_user = __esm({
|
|
14644
|
-
"src/core/editor/hooks/use-user.ts"() {
|
|
14645
|
-
"use strict";
|
|
14646
|
-
}
|
|
14647
|
-
});
|
|
14648
|
-
|
|
14649
14652
|
// src/core/editor/components/preview.tsx
|
|
14650
14653
|
function Preview() {
|
|
14651
|
-
const {
|
|
14652
|
-
const { template, setPreviewMode, previewMode } = useEditorStore();
|
|
14654
|
+
const { template, setPreviewMode, previewMode, isPaidLevel } = useEditorStore();
|
|
14653
14655
|
const [html, setHtml] = (0, import_react33.useState)("");
|
|
14654
14656
|
const [isLoading, setIsLoading] = (0, import_react33.useState)(false);
|
|
14655
14657
|
const lastTemplateRef = (0, import_react33.useRef)("");
|
|
@@ -14669,7 +14671,7 @@ function Preview() {
|
|
|
14669
14671
|
setIsLoading(true);
|
|
14670
14672
|
const convertMjml = async () => {
|
|
14671
14673
|
try {
|
|
14672
|
-
const mjmlString = json2mjml(template, "production", {
|
|
14674
|
+
const mjmlString = json2mjml(template, "production", { isPaidLevel });
|
|
14673
14675
|
console.log("MJML string:", mjmlString);
|
|
14674
14676
|
const result = await compileMjml(mjmlString);
|
|
14675
14677
|
if (result.errors?.length > 0) {
|
|
@@ -14717,7 +14719,6 @@ var init_preview = __esm({
|
|
|
14717
14719
|
init_editor();
|
|
14718
14720
|
init_json2mjml();
|
|
14719
14721
|
init_useMjmlCompiler();
|
|
14720
|
-
init_use_user();
|
|
14721
14722
|
import_react33 = require("react");
|
|
14722
14723
|
init_tooltip();
|
|
14723
14724
|
import_jsx_runtime47 = require("react/jsx-runtime");
|
|
@@ -16444,15 +16445,14 @@ var StylesPage = ({ value }) => {
|
|
|
16444
16445
|
};
|
|
16445
16446
|
|
|
16446
16447
|
// src/core/editor/components/email-template-v2/components/styles-badge.tsx
|
|
16447
|
-
init_use_user();
|
|
16448
16448
|
init_editor();
|
|
16449
16449
|
var import_react40 = require("react");
|
|
16450
16450
|
var import_lucide_react38 = require("lucide-react");
|
|
16451
16451
|
var import_lucide_react39 = require("lucide-react");
|
|
16452
16452
|
var import_jsx_runtime67 = require("react/jsx-runtime");
|
|
16453
16453
|
var StylesBadge = ({ value }) => {
|
|
16454
|
-
const {
|
|
16455
|
-
const
|
|
16454
|
+
const { template, setShowCompanyFooter, isPaidLevel } = useEditorStore();
|
|
16455
|
+
const isPaid = isPaidLevel >= 1;
|
|
16456
16456
|
const showCompanyFooter = (0, import_react40.useMemo)(() => {
|
|
16457
16457
|
return template.content[0]?.data?.value?.showCompanyFooter ?? true;
|
|
16458
16458
|
}, [template]);
|
|
@@ -23764,15 +23764,25 @@ var Editor2 = (0, import_react80.lazy)(() => Promise.resolve().then(() => (init_
|
|
|
23764
23764
|
function TemplatePage({
|
|
23765
23765
|
templateId,
|
|
23766
23766
|
initialTemplate,
|
|
23767
|
-
onSave
|
|
23767
|
+
onSave,
|
|
23768
|
+
isPaidLevel = 0
|
|
23768
23769
|
}) {
|
|
23770
|
+
console.log("Template page rendered!");
|
|
23771
|
+
if (isPaidLevel > MAX_PAID_LEVEL) {
|
|
23772
|
+
console.warn("This paid level is invalid:", isPaidLevel);
|
|
23773
|
+
} else {
|
|
23774
|
+
console.log("This paid level is valid:", isPaidLevel);
|
|
23775
|
+
}
|
|
23769
23776
|
(0, import_react80.useState)(() => {
|
|
23770
|
-
useEditorStore.getState().initializeWithTemplate(templateId, initialTemplate, onSave);
|
|
23777
|
+
useEditorStore.getState().initializeWithTemplate(templateId, initialTemplate, onSave, isPaidLevel);
|
|
23771
23778
|
});
|
|
23772
23779
|
useAutoSave();
|
|
23773
23780
|
const [editorLoading, setEditorLoading] = (0, import_react80.useState)(false);
|
|
23774
23781
|
const [isPageHovered, setIsPageHovered] = (0, import_react80.useState)(false);
|
|
23775
|
-
const
|
|
23782
|
+
const template = useEditorStore((state) => state.template);
|
|
23783
|
+
const previewMode = useEditorStore((state) => state.previewMode);
|
|
23784
|
+
const focusIdx = useEditorStore((state) => state.focusIdx);
|
|
23785
|
+
const setFocusIdx = useEditorStore((state) => state.setFocusIdx);
|
|
23776
23786
|
const canvasBackgroundColor = template?.content?.[0]?.attributes?.["background-color"] || "#ffffff";
|
|
23777
23787
|
const handlePageClick = (e) => {
|
|
23778
23788
|
if (!previewMode && e.target === e.currentTarget) {
|