@markopolo_ai_inc/markopolo-email-editor 1.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +63 -0
- package/build/asset-manifest.json +12 -0
- package/build/favicon.ico +0 -0
- package/build/index.html +1 -0
- package/build/logo192.png +0 -0
- package/build/logo512.png +0 -0
- package/build/manifest.json +25 -0
- package/build/robots.txt +3 -0
- package/build/static/css/main.588cb535.css +9 -0
- package/build/static/js/206.a4343501.chunk.js +1 -0
- package/build/static/js/main.053d366a.js +2 -0
- package/build/static/js/main.053d366a.js.LICENSE.txt +56 -0
- package/package.json +64 -0
- package/public/favicon.ico +0 -0
- package/public/index.html +50 -0
- package/public/logo192.png +0 -0
- package/public/logo512.png +0 -0
- package/public/manifest.json +25 -0
- package/public/robots.txt +3 -0
- package/src/App.js +15 -0
- package/src/components/EmailEditor/assets/App.css +339 -0
- package/src/components/EmailEditor/assets/Columns.css +309 -0
- package/src/components/EmailEditor/assets/Header.css +174 -0
- package/src/components/EmailEditor/assets/ImageBlock.css +12 -0
- package/src/components/EmailEditor/assets/Preview.css +30 -0
- package/src/components/EmailEditor/assets/RichText.css +199 -0
- package/src/components/EmailEditor/assets/RightSettings.css +520 -0
- package/src/components/EmailEditor/assets/Sidebar.css +195 -0
- package/src/components/EmailEditor/components/BlockItems/ButtonBlock.js +25 -0
- package/src/components/EmailEditor/components/BlockItems/DividerBlock.js +19 -0
- package/src/components/EmailEditor/components/BlockItems/HeadingBlock.js +16 -0
- package/src/components/EmailEditor/components/BlockItems/ImageBlock.js +28 -0
- package/src/components/EmailEditor/components/BlockItems/MenuBlock.js +52 -0
- package/src/components/EmailEditor/components/BlockItems/SocialLinkBlocks.js +26 -0
- package/src/components/EmailEditor/components/BlockItems/SpacerBlock.js +23 -0
- package/src/components/EmailEditor/components/BlockItems/TextBlock.js +16 -0
- package/src/components/EmailEditor/components/BlockItems/index.js +25 -0
- package/src/components/EmailEditor/components/ColorPicker/index.js +26 -0
- package/src/components/EmailEditor/components/Column/index.js +253 -0
- package/src/components/EmailEditor/components/Header/index.js +243 -0
- package/src/components/EmailEditor/components/LeftSideBar/index.js +253 -0
- package/src/components/EmailEditor/components/Main/index.js +281 -0
- package/src/components/EmailEditor/components/Preview/index.js +97 -0
- package/src/components/EmailEditor/components/RichText/Bold.js +37 -0
- package/src/components/EmailEditor/components/RichText/FontColor.js +39 -0
- package/src/components/EmailEditor/components/RichText/InsertOrderedList.js +36 -0
- package/src/components/EmailEditor/components/RichText/InsertUnorderedList.js +36 -0
- package/src/components/EmailEditor/components/RichText/Italic.js +36 -0
- package/src/components/EmailEditor/components/RichText/Link.js +99 -0
- package/src/components/EmailEditor/components/RichText/RichTextLayout.js +53 -0
- package/src/components/EmailEditor/components/RichText/Strikethrough.js +36 -0
- package/src/components/EmailEditor/components/RichText/TextAlign.js +58 -0
- package/src/components/EmailEditor/components/RichText/Underline.js +36 -0
- package/src/components/EmailEditor/components/RichText/index.js +210 -0
- package/src/components/EmailEditor/components/RightSetting/index.js +126 -0
- package/src/components/EmailEditor/components/StyleSettings/ButtonStyleSettings.js +141 -0
- package/src/components/EmailEditor/components/StyleSettings/ColumnStyleSettings.js +241 -0
- package/src/components/EmailEditor/components/StyleSettings/DividerStyleSettings.js +111 -0
- package/src/components/EmailEditor/components/StyleSettings/HeadingStyleSettings.js +162 -0
- package/src/components/EmailEditor/components/StyleSettings/ImageStyleSettings.js +217 -0
- package/src/components/EmailEditor/components/StyleSettings/MenuStyleSettings.js +177 -0
- package/src/components/EmailEditor/components/StyleSettings/PaddingSettings.js +30 -0
- package/src/components/EmailEditor/components/StyleSettings/SocialLinkSettings.js +250 -0
- package/src/components/EmailEditor/components/StyleSettings/SpacerStyleSettings.js +38 -0
- package/src/components/EmailEditor/components/StyleSettings/TextStyleSettings.js +108 -0
- package/src/components/EmailEditor/components/StyleSettings/index.js +32 -0
- package/src/components/EmailEditor/configs/getBlockConfigsList.js +263 -0
- package/src/components/EmailEditor/configs/getColumnConfigFunc.js +59 -0
- package/src/components/EmailEditor/configs/getColumnsSettings.js +246 -0
- package/src/components/EmailEditor/configs/useDataSource.js +19 -0
- package/src/components/EmailEditor/index.js +93 -0
- package/src/components/EmailEditor/reducers/index.js +173 -0
- package/src/components/EmailEditor/translation/en.js +166 -0
- package/src/components/EmailEditor/translation/index.js +39 -0
- package/src/components/EmailEditor/translation/zh.js +166 -0
- package/src/components/EmailEditor/utils/classNames.js +5 -0
- package/src/components/EmailEditor/utils/dataToHTML.js +323 -0
- package/src/components/EmailEditor/utils/exportValidation.js +296 -0
- package/src/components/EmailEditor/utils/helpers.js +48 -0
- package/src/components/EmailEditor/utils/pexels.js +20 -0
- package/src/components/EmailEditor/utils/useSection.js +24 -0
- package/src/components/EmailEditor/utils/useStyleLayout.js +82 -0
- package/src/index.css +99 -0
- package/src/index.js +15 -0
- package/src/logo.svg +1 -0
- package/src/pages/AppPage/index.js +10 -0
- package/src/pages/Dashboard/Header.js +192 -0
- package/src/pages/Dashboard/defaultBlockList.json +1758 -0
- package/src/pages/Dashboard/index.js +48 -0
- package/src/reportWebVitals.js +13 -0
- package/src/setupTests.js +5 -0
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { useContext } from "react";
|
|
2
|
+
import { GlobalContext } from "../../reducers";
|
|
3
|
+
import RichTextLayout from "../RichText/RichTextLayout";
|
|
4
|
+
import { useMemo } from "react";
|
|
5
|
+
|
|
6
|
+
const ButtonBlock = (props) => {
|
|
7
|
+
const { blockItem, index } = props;
|
|
8
|
+
const { currentItem, previewMode } = useContext(GlobalContext);
|
|
9
|
+
|
|
10
|
+
// TODO: border radius not yet implemented
|
|
11
|
+
const isEdit = currentItem && currentItem.index === index;
|
|
12
|
+
const styles = previewMode === "desktop" ? blockItem.styles.desktop : { ...blockItem.styles.desktop, ...blockItem.styles.mobile };
|
|
13
|
+
|
|
14
|
+
const contentStyles =
|
|
15
|
+
previewMode === "desktop" ? blockItem.contentStyles?.desktop : { ...blockItem.contentStyles?.desktop, ...blockItem.contentStyles?.mobile };
|
|
16
|
+
|
|
17
|
+
const richTextElement = useMemo(() => <RichTextLayout {...props} />, [props]);
|
|
18
|
+
return (
|
|
19
|
+
<div style={{ ...contentStyles }}>
|
|
20
|
+
{isEdit ? richTextElement : <div style={{ ...styles }} dangerouslySetInnerHTML={{ __html: blockItem.text }}></div>}
|
|
21
|
+
</div>
|
|
22
|
+
);
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
export default ButtonBlock;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { useContext } from "react";
|
|
2
|
+
import { GlobalContext } from "../../reducers";
|
|
3
|
+
|
|
4
|
+
const DividerBlock = ({ blockItem }) => {
|
|
5
|
+
const { previewMode } = useContext(GlobalContext);
|
|
6
|
+
const styles = previewMode === "desktop" ? blockItem.styles.desktop : { ...blockItem.styles.desktop, ...blockItem.styles.mobile };
|
|
7
|
+
const contentStyles =
|
|
8
|
+
previewMode === "desktop" ? blockItem.contentStyles?.desktop : { ...blockItem.contentStyles?.desktop, ...blockItem.contentStyles?.mobile };
|
|
9
|
+
|
|
10
|
+
return (
|
|
11
|
+
<div className="relative">
|
|
12
|
+
<div style={{ ...contentStyles }}>
|
|
13
|
+
<div style={{ ...styles }}></div>
|
|
14
|
+
</div>
|
|
15
|
+
</div>
|
|
16
|
+
);
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
export default DividerBlock;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { useContext } from "react";
|
|
2
|
+
import { GlobalContext } from "../../reducers";
|
|
3
|
+
import RichTextLayout from "../RichText/RichTextLayout";
|
|
4
|
+
import { useMemo } from "react";
|
|
5
|
+
|
|
6
|
+
const HeadingBlock = (props) => {
|
|
7
|
+
const { index, blockItem } = props;
|
|
8
|
+
const { currentItem, previewMode } = useContext(GlobalContext);
|
|
9
|
+
const styles = previewMode === "desktop" ? blockItem.styles.desktop : { ...blockItem.styles.desktop, ...blockItem.styles.mobile };
|
|
10
|
+
const isEdit = currentItem && currentItem.index === index;
|
|
11
|
+
const richTextElement = useMemo(() => <RichTextLayout {...props} />, [props]);
|
|
12
|
+
|
|
13
|
+
return isEdit ? richTextElement : <div style={{ ...styles }} dangerouslySetInnerHTML={{ __html: blockItem.text }}></div>;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
export default HeadingBlock;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { useContext } from "react";
|
|
2
|
+
import { GlobalContext } from "../../reducers";
|
|
3
|
+
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
|
4
|
+
import { faImage } from "@fortawesome/free-solid-svg-icons";
|
|
5
|
+
|
|
6
|
+
const ImageBlock = ({ blockItem }) => {
|
|
7
|
+
const { src, alt } = blockItem;
|
|
8
|
+
const { previewMode } = useContext(GlobalContext);
|
|
9
|
+
const styles = previewMode === "desktop" ? blockItem.styles.desktop : { ...blockItem.styles.desktop, ...blockItem.styles.mobile };
|
|
10
|
+
const contentStyles =
|
|
11
|
+
previewMode === "desktop" ? blockItem.contentStyles?.desktop : { ...blockItem.contentStyles?.desktop, ...blockItem.contentStyles?.mobile };
|
|
12
|
+
|
|
13
|
+
return (
|
|
14
|
+
<div className="relative">
|
|
15
|
+
<div style={{ ...contentStyles }}>
|
|
16
|
+
{src ? (
|
|
17
|
+
<img src={src} style={styles} alt={alt} className="inline-block" />
|
|
18
|
+
) : (
|
|
19
|
+
<div className="empty-image" style={{ ...styles, width: styles.width === "auto" ? "100%" : styles.width }}>
|
|
20
|
+
<FontAwesomeIcon icon={faImage} className="empty-image-icon" />
|
|
21
|
+
</div>
|
|
22
|
+
)}
|
|
23
|
+
</div>
|
|
24
|
+
</div>
|
|
25
|
+
);
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
export default ImageBlock;
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { useContext } from "react";
|
|
2
|
+
import { GlobalContext } from "../../reducers";
|
|
3
|
+
|
|
4
|
+
const MenuBlock = ({ blockItem }) => {
|
|
5
|
+
const { previewMode } = useContext(GlobalContext);
|
|
6
|
+
const { list = [], separator = " | " } = blockItem;
|
|
7
|
+
const styles = previewMode === "desktop" ? blockItem.styles.desktop : { ...blockItem.styles.desktop, ...blockItem.styles.mobile };
|
|
8
|
+
const contentStyles =
|
|
9
|
+
previewMode === "desktop" ? blockItem.contentStyles?.desktop : { ...blockItem.contentStyles?.desktop, ...blockItem.contentStyles?.mobile };
|
|
10
|
+
|
|
11
|
+
const linkStyle = {
|
|
12
|
+
fontSize: styles.fontSize != null ? `${styles.fontSize}px` : "14px",
|
|
13
|
+
fontFamily: styles.fontFamily || "sans-serif",
|
|
14
|
+
color: styles.linkColor || styles.color || "#2faade",
|
|
15
|
+
fontWeight: styles.fontWeight || "normal",
|
|
16
|
+
letterSpacing: styles.letterSpacing != null ? `${styles.letterSpacing}px` : "0px",
|
|
17
|
+
textDecoration: "none",
|
|
18
|
+
paddingLeft: styles.paddingLeft != null ? `${styles.paddingLeft}px` : "8px",
|
|
19
|
+
paddingRight: styles.paddingRight != null ? `${styles.paddingRight}px` : "8px",
|
|
20
|
+
paddingTop: styles.paddingTop != null ? `${styles.paddingTop}px` : "4px",
|
|
21
|
+
paddingBottom: styles.paddingBottom != null ? `${styles.paddingBottom}px` : "4px",
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
const separatorStyle = {
|
|
25
|
+
fontSize: styles.fontSize != null ? `${styles.fontSize}px` : "14px",
|
|
26
|
+
color: styles.color || "#333",
|
|
27
|
+
paddingLeft: 4,
|
|
28
|
+
paddingRight: 4,
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
return (
|
|
32
|
+
<div className="relative">
|
|
33
|
+
<div style={{ ...contentStyles, display: "flex", flexWrap: "wrap", alignItems: "center", justifyContent: contentStyles.textAlign || "center" }}>
|
|
34
|
+
{list.map((item, index) => (
|
|
35
|
+
<span key={index} style={{ display: "inline-flex", alignItems: "center" }}>
|
|
36
|
+
{index > 0 && <span style={separatorStyle}>{separator}</span>}
|
|
37
|
+
<a
|
|
38
|
+
href={item.url ? (item.url.startsWith("http") ? item.url : `https://${item.url}`) : "#"}
|
|
39
|
+
target={item.target === "_blank" ? "_blank" : "_self"}
|
|
40
|
+
rel={item.target === "_blank" ? "noopener noreferrer" : undefined}
|
|
41
|
+
style={linkStyle}
|
|
42
|
+
>
|
|
43
|
+
{item.label || "Link"}
|
|
44
|
+
</a>
|
|
45
|
+
</span>
|
|
46
|
+
))}
|
|
47
|
+
</div>
|
|
48
|
+
</div>
|
|
49
|
+
);
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
export default MenuBlock;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { useContext } from "react";
|
|
2
|
+
import { GlobalContext } from "../../reducers";
|
|
3
|
+
|
|
4
|
+
const SocialLinkBlocks = ({ blockItem }) => {
|
|
5
|
+
const { previewMode } = useContext(GlobalContext);
|
|
6
|
+
const { list, imageWidth } = blockItem;
|
|
7
|
+
const styles = previewMode === "desktop" ? blockItem.styles.desktop : { ...blockItem.styles.desktop, ...blockItem.styles.mobile };
|
|
8
|
+
const contentStyles =
|
|
9
|
+
previewMode === "desktop" ? blockItem.contentStyles?.desktop : { ...blockItem.contentStyles?.desktop, ...blockItem.contentStyles?.mobile };
|
|
10
|
+
return (
|
|
11
|
+
<div className="relative">
|
|
12
|
+
<div style={contentStyles}>
|
|
13
|
+
{list.map((socialLinkItem, index) => {
|
|
14
|
+
const { image, title } = socialLinkItem;
|
|
15
|
+
return (
|
|
16
|
+
<div key={index} style={{ ...styles, display: "inline-block" }}>
|
|
17
|
+
<img src={image} alt={title} style={{ width: imageWidth }} />
|
|
18
|
+
</div>
|
|
19
|
+
);
|
|
20
|
+
})}
|
|
21
|
+
</div>
|
|
22
|
+
</div>
|
|
23
|
+
);
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
export default SocialLinkBlocks;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { useContext } from "react";
|
|
2
|
+
import { GlobalContext } from "../../reducers";
|
|
3
|
+
|
|
4
|
+
const SpacerBlock = ({ blockItem }) => {
|
|
5
|
+
const { previewMode } = useContext(GlobalContext);
|
|
6
|
+
const styles = previewMode === "desktop" ? blockItem.styles.desktop : { ...blockItem.styles.desktop, ...blockItem.styles.mobile };
|
|
7
|
+
const contentStyles =
|
|
8
|
+
previewMode === "desktop" ? blockItem.contentStyles?.desktop : { ...blockItem.contentStyles?.desktop, ...blockItem.contentStyles?.mobile };
|
|
9
|
+
|
|
10
|
+
return (
|
|
11
|
+
<div className="relative" style={{ ...contentStyles }}>
|
|
12
|
+
<div
|
|
13
|
+
style={{
|
|
14
|
+
width: "100%",
|
|
15
|
+
height: typeof styles.height === "number" ? `${styles.height}px` : styles.height || "16px",
|
|
16
|
+
display: styles.display || "block",
|
|
17
|
+
}}
|
|
18
|
+
/>
|
|
19
|
+
</div>
|
|
20
|
+
);
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
export default SpacerBlock;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { useContext } from "react";
|
|
2
|
+
import { GlobalContext } from "../../reducers";
|
|
3
|
+
import RichTextLayout from "../RichText/RichTextLayout";
|
|
4
|
+
import { useMemo } from "react";
|
|
5
|
+
|
|
6
|
+
const TextBlock = (props) => {
|
|
7
|
+
const { index, blockItem } = props;
|
|
8
|
+
const { currentItem, previewMode } = useContext(GlobalContext);
|
|
9
|
+
const styles = previewMode === "desktop" ? blockItem.styles.desktop : { ...blockItem.styles.desktop, ...blockItem.styles.mobile };
|
|
10
|
+
const isEdit = currentItem && currentItem.index === index;
|
|
11
|
+
const richTextElement = useMemo(() => <RichTextLayout {...props} />, [props]);
|
|
12
|
+
|
|
13
|
+
return isEdit ? richTextElement : <div style={{ ...styles }} dangerouslySetInnerHTML={{ __html: blockItem.text }}></div>;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
export default TextBlock;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import TextBlock from "./TextBlock";
|
|
2
|
+
import HeadingBlock from "./HeadingBlock";
|
|
3
|
+
import ButtonBlock from "./ButtonBlock";
|
|
4
|
+
import DividerBlock from "./DividerBlock";
|
|
5
|
+
import SpacerBlock from "./SpacerBlock";
|
|
6
|
+
import MenuBlock from "./MenuBlock";
|
|
7
|
+
import ImageBlock from "./ImageBlock";
|
|
8
|
+
import SocialLinkBlocks from "./SocialLinkBlocks";
|
|
9
|
+
|
|
10
|
+
const BlockItems = ({ blockItem, index }) => {
|
|
11
|
+
return (
|
|
12
|
+
<>
|
|
13
|
+
{blockItem && blockItem.key === "text" && <TextBlock blockItem={blockItem} index={index} />}
|
|
14
|
+
{blockItem && blockItem.key === "heading" && <HeadingBlock blockItem={blockItem} index={index} />}
|
|
15
|
+
{blockItem && blockItem.key === "button" && <ButtonBlock blockItem={blockItem} index={index} />}
|
|
16
|
+
{blockItem && blockItem.key === "divider" && <DividerBlock blockItem={blockItem} index={index} />}
|
|
17
|
+
{blockItem && blockItem.key === "spacer" && <SpacerBlock blockItem={blockItem} index={index} />}
|
|
18
|
+
{blockItem && blockItem.key === "menu" && <MenuBlock blockItem={blockItem} index={index} />}
|
|
19
|
+
{blockItem && blockItem.key === "image" && <ImageBlock blockItem={blockItem} index={index} />}
|
|
20
|
+
{blockItem && blockItem.key === "social_link" && <SocialLinkBlocks blockItem={blockItem} index={index} />}
|
|
21
|
+
</>
|
|
22
|
+
);
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
export default BlockItems;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { useState } from "react";
|
|
2
|
+
import { Popover } from "antd";
|
|
3
|
+
import { ChromePicker } from "react-color";
|
|
4
|
+
|
|
5
|
+
const ColorPicker = ({ color, setColor }) => {
|
|
6
|
+
const [popoverOpen, setPopoverOpen] = useState(false);
|
|
7
|
+
|
|
8
|
+
return (
|
|
9
|
+
<Popover
|
|
10
|
+
zIndex={1070}
|
|
11
|
+
getPopupContainer={() => document.querySelector(".right-settings") || document.body}
|
|
12
|
+
content={
|
|
13
|
+
<div className="select-none ee-color-picker-popover">
|
|
14
|
+
<ChromePicker color={color} style={{ boxShadow: "none" }} onChange={setColor} />
|
|
15
|
+
</div>
|
|
16
|
+
}
|
|
17
|
+
trigger="click"
|
|
18
|
+
open={popoverOpen}
|
|
19
|
+
onOpenChange={setPopoverOpen}
|
|
20
|
+
>
|
|
21
|
+
<button className="color-picker-button" style={{ background: color }}></button>
|
|
22
|
+
</Popover>
|
|
23
|
+
);
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
export default ColorPicker;
|
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
import { Fragment, useContext } from "react";
|
|
2
|
+
import { GlobalContext } from "../../reducers";
|
|
3
|
+
import classNames from "../../utils/classNames";
|
|
4
|
+
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
|
5
|
+
import { faArrowsAlt, faTrash } from "@fortawesome/free-solid-svg-icons";
|
|
6
|
+
import { deepClone } from "../../utils/helpers";
|
|
7
|
+
import BlockItems from "../BlockItems";
|
|
8
|
+
import useTranslation from "../../translation";
|
|
9
|
+
|
|
10
|
+
const Column = (props) => {
|
|
11
|
+
const { block, blockIndex, clearStyles } = props;
|
|
12
|
+
const { t } = useTranslation();
|
|
13
|
+
const { previewMode, blockList, currentItem, setBlockList, setCurrentItem, setIsDragStart, isDragStart, bodySettings, setActionType } =
|
|
14
|
+
useContext(GlobalContext);
|
|
15
|
+
|
|
16
|
+
let columnStyles = previewMode === "desktop" ? block.styles.desktop : { ...block.styles.desktop, ...block.styles.mobile };
|
|
17
|
+
const { contentBackground, ...newStyles } = columnStyles;
|
|
18
|
+
|
|
19
|
+
const deleteBlock = (event) => {
|
|
20
|
+
event.preventDefault();
|
|
21
|
+
event.stopPropagation();
|
|
22
|
+
const newBlockList = deepClone(blockList);
|
|
23
|
+
newBlockList.splice(blockIndex, 1);
|
|
24
|
+
setBlockList(newBlockList, "delete");
|
|
25
|
+
setCurrentItem(null);
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
const deleteBlockItem = (index) => (event) => {
|
|
29
|
+
event.preventDefault();
|
|
30
|
+
event.stopPropagation();
|
|
31
|
+
const newBlockList = deepClone(blockList);
|
|
32
|
+
const indexArr = index.split("-");
|
|
33
|
+
const blockIndex = indexArr[0];
|
|
34
|
+
const contentIndex = indexArr[1];
|
|
35
|
+
const itemIndex = indexArr[2];
|
|
36
|
+
const item = newBlockList[blockIndex].children[contentIndex].children;
|
|
37
|
+
item.splice(itemIndex, 1);
|
|
38
|
+
if (item.length === 0) {
|
|
39
|
+
item.push({
|
|
40
|
+
name: t("drag_block_here"),
|
|
41
|
+
key: "empty",
|
|
42
|
+
width: "100%",
|
|
43
|
+
styles: {
|
|
44
|
+
desktop: {
|
|
45
|
+
backgroundColor: "transparent",
|
|
46
|
+
paddingTop: 0,
|
|
47
|
+
paddingLeft: 0,
|
|
48
|
+
paddingRight: 0,
|
|
49
|
+
paddingBottom: 0,
|
|
50
|
+
},
|
|
51
|
+
mobile: {},
|
|
52
|
+
},
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
setBlockList(newBlockList, "delete");
|
|
56
|
+
setCurrentItem(null);
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
const dragStart = () => {
|
|
60
|
+
setIsDragStart(true);
|
|
61
|
+
setCurrentItem({ ...currentItem, type: "move" });
|
|
62
|
+
setActionType("move");
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
const dragEnd = (event) => {
|
|
66
|
+
event.preventDefault();
|
|
67
|
+
event.stopPropagation();
|
|
68
|
+
// Parent onDrop/onDragOver use 30ms delay; delay 50ms here so styles are cleared after blockList/currentItem update
|
|
69
|
+
setTimeout(() => {
|
|
70
|
+
event.target.style.border = "";
|
|
71
|
+
event.target.children[0] && event.target.children[0].classList.remove("sidebar-block-move");
|
|
72
|
+
setIsDragStart(false);
|
|
73
|
+
clearStyles();
|
|
74
|
+
}, 50);
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
const blockItemElement = (item, index) => {
|
|
78
|
+
const nextIndex = index
|
|
79
|
+
.split("-")
|
|
80
|
+
.map((item, itemIndex) => {
|
|
81
|
+
if (itemIndex === 2) {
|
|
82
|
+
return Number(item) + 1;
|
|
83
|
+
} else {
|
|
84
|
+
return item;
|
|
85
|
+
}
|
|
86
|
+
})
|
|
87
|
+
.join("-");
|
|
88
|
+
return (
|
|
89
|
+
<>
|
|
90
|
+
{item.key === "empty" ? (
|
|
91
|
+
<div className="block-empty-content p-4 h-32 relative width-full">
|
|
92
|
+
{isDragStart && currentItem && currentItem.data.key !== "column" && (
|
|
93
|
+
<div className="block-empty-content-tools" onDragOver={preventDefault} data-index={index} data-type="empty-block-item">
|
|
94
|
+
{t("drag_block_here")}
|
|
95
|
+
</div>
|
|
96
|
+
)}
|
|
97
|
+
{item.name}
|
|
98
|
+
</div>
|
|
99
|
+
) : (
|
|
100
|
+
<>
|
|
101
|
+
<div className="relative block-content-drag-label-content" data-index={index} data-type="block-item">
|
|
102
|
+
<div className="absolute block-move-top">
|
|
103
|
+
<span className="block-tools-drag_here">{t("drag_block_here")}</span>
|
|
104
|
+
</div>
|
|
105
|
+
</div>
|
|
106
|
+
<div className="block-item">
|
|
107
|
+
<div className={classNames("relative", currentItem && currentItem.index === index && "block-item-focus")}>
|
|
108
|
+
<div
|
|
109
|
+
className="block-item-tools"
|
|
110
|
+
onDragOver={preventDefault}
|
|
111
|
+
onClick={(event) => {
|
|
112
|
+
event.preventDefault();
|
|
113
|
+
event.stopPropagation();
|
|
114
|
+
setCurrentItem({ data: item, type: "edit", index });
|
|
115
|
+
setActionType("edit");
|
|
116
|
+
}}
|
|
117
|
+
>
|
|
118
|
+
{isDragStart && currentItem && currentItem.data.key !== "column" && (
|
|
119
|
+
<>
|
|
120
|
+
<div
|
|
121
|
+
className="block-move-content-top"
|
|
122
|
+
data-name="dragTools"
|
|
123
|
+
data-position="top"
|
|
124
|
+
data-index={index}
|
|
125
|
+
data-type="block-item-move"
|
|
126
|
+
></div>
|
|
127
|
+
<div
|
|
128
|
+
className="block-move-content-bottom"
|
|
129
|
+
data-name="dragTools"
|
|
130
|
+
data-position="bottom"
|
|
131
|
+
data-index={nextIndex}
|
|
132
|
+
data-type="block-item-move"
|
|
133
|
+
></div>
|
|
134
|
+
</>
|
|
135
|
+
)}
|
|
136
|
+
<span className="absolute block-item-delete" onClick={deleteBlockItem(index)}>
|
|
137
|
+
<FontAwesomeIcon icon={faTrash} />
|
|
138
|
+
</span>
|
|
139
|
+
<span
|
|
140
|
+
className="absolute block-item-move current-move-block-arrows"
|
|
141
|
+
draggable="true"
|
|
142
|
+
onDragEnd={dragEnd}
|
|
143
|
+
data-index={index}
|
|
144
|
+
onDragStart={dragStart}
|
|
145
|
+
>
|
|
146
|
+
<FontAwesomeIcon icon={faArrowsAlt} />
|
|
147
|
+
</span>
|
|
148
|
+
</div>
|
|
149
|
+
<BlockItems blockItem={item} index={index} />
|
|
150
|
+
</div>
|
|
151
|
+
</div>
|
|
152
|
+
</>
|
|
153
|
+
)}
|
|
154
|
+
</>
|
|
155
|
+
);
|
|
156
|
+
};
|
|
157
|
+
|
|
158
|
+
const preventDefault = (event) => {
|
|
159
|
+
event.preventDefault();
|
|
160
|
+
};
|
|
161
|
+
|
|
162
|
+
return (
|
|
163
|
+
<>
|
|
164
|
+
<div className="relative block-drag-label-content" data-index={blockIndex} data-position="top">
|
|
165
|
+
<div className="absolute block-move-top">
|
|
166
|
+
<span className="block-tools-drag_here">{t("drag_block_here")}</span>
|
|
167
|
+
</div>
|
|
168
|
+
<div
|
|
169
|
+
className={classNames(
|
|
170
|
+
"relative block",
|
|
171
|
+
currentItem && currentItem.index === blockIndex && "block-focus",
|
|
172
|
+
previewMode === "mobile" && "mobile-block-focus"
|
|
173
|
+
)}
|
|
174
|
+
onClick={(event) => {
|
|
175
|
+
event.preventDefault();
|
|
176
|
+
event.stopPropagation();
|
|
177
|
+
setCurrentItem({ data: block, type: "edit", index: blockIndex });
|
|
178
|
+
}}
|
|
179
|
+
>
|
|
180
|
+
<div className="hover-visible">
|
|
181
|
+
<span className="absolute block-delete" onClick={deleteBlock}>
|
|
182
|
+
<FontAwesomeIcon icon={faTrash} />
|
|
183
|
+
</span>
|
|
184
|
+
<span
|
|
185
|
+
className="absolute block-move current-move-block-arrows"
|
|
186
|
+
draggable="true"
|
|
187
|
+
onDragEnd={dragEnd}
|
|
188
|
+
data-index={blockIndex}
|
|
189
|
+
onDragStart={dragStart}
|
|
190
|
+
>
|
|
191
|
+
<FontAwesomeIcon icon={faArrowsAlt} />
|
|
192
|
+
</span>
|
|
193
|
+
</div>
|
|
194
|
+
|
|
195
|
+
<div className="width-full height-full absolute" onDragOver={preventDefault}>
|
|
196
|
+
{isDragStart && currentItem.data.key === "column" && (
|
|
197
|
+
<>
|
|
198
|
+
<div
|
|
199
|
+
className="block-move-content-top"
|
|
200
|
+
data-name="dragTools"
|
|
201
|
+
data-type="drag-over-column"
|
|
202
|
+
data-position="top"
|
|
203
|
+
data-index={blockIndex}
|
|
204
|
+
></div>
|
|
205
|
+
<div
|
|
206
|
+
className="block-move-content-bottom"
|
|
207
|
+
data-name="dragTools"
|
|
208
|
+
data-type="drag-over-column"
|
|
209
|
+
data-position="bottom"
|
|
210
|
+
data-index={blockIndex + 1}
|
|
211
|
+
></div>
|
|
212
|
+
</>
|
|
213
|
+
)}
|
|
214
|
+
</div>
|
|
215
|
+
<div className="column margin-auto" style={{ ...newStyles, maxWidth: "100%" }}>
|
|
216
|
+
<div className="block-content" style={{ background: contentBackground, width: bodySettings.contentWidth }} data-index={blockIndex}>
|
|
217
|
+
{block.children.map((content, index) => {
|
|
218
|
+
let contentStyles = previewMode === "desktop" ? content.styles.desktop : { ...content.styles.desktop, ...content.styles.mobile };
|
|
219
|
+
return (
|
|
220
|
+
<Fragment key={index}>
|
|
221
|
+
<div
|
|
222
|
+
id={`block-content-${blockIndex}-${index}`}
|
|
223
|
+
style={{ ...contentStyles, width: previewMode === "mobile" ? "100%" : content.width }}
|
|
224
|
+
>
|
|
225
|
+
{content.children.map((item, itemIndex) => {
|
|
226
|
+
const blockItemIndex = `${blockIndex}-${index}-${itemIndex}`;
|
|
227
|
+
const isLastKid = itemIndex === content.children.length - 1 && item.key !== "empty";
|
|
228
|
+
return (
|
|
229
|
+
<Fragment key={itemIndex}>
|
|
230
|
+
{blockItemElement(item, blockItemIndex)}
|
|
231
|
+
{isLastKid && (
|
|
232
|
+
<div className="relative block-content-drag-label-content" data-index={`${blockIndex}-${index}-${itemIndex + 1}`}>
|
|
233
|
+
<div className="absolute block-move-bottom">
|
|
234
|
+
<span className="block-tools-drag_here">{t("drag_block_here")}</span>
|
|
235
|
+
</div>
|
|
236
|
+
</div>
|
|
237
|
+
)}
|
|
238
|
+
</Fragment>
|
|
239
|
+
);
|
|
240
|
+
})}
|
|
241
|
+
</div>
|
|
242
|
+
</Fragment>
|
|
243
|
+
);
|
|
244
|
+
})}
|
|
245
|
+
</div>
|
|
246
|
+
</div>
|
|
247
|
+
</div>
|
|
248
|
+
</div>
|
|
249
|
+
</>
|
|
250
|
+
);
|
|
251
|
+
};
|
|
252
|
+
|
|
253
|
+
export default Column;
|