const toolTemplate = function (values, isViewer = false) {
return `<div class="product-card-main" ${values.productBackground};border-color:${
values.productBorderColor
}; border-width: ${values.productBorderSize}px;">
<img src="${values.productImage.url}" />
<div class="product-card-body"
values.productTitleFontSize
}px">
<h3 class="m-0">${values.productTitle}</h3>
<div class="description" !important;font-size:${
values.productDescriptionFontSize
}px">${values.productDescription}</div>
</div>
<div class="product-footer ${
values.productCTAPositi true ? "horizontal" : "vertical"
}" ${values.productPriceBackgroundColor};">
<div ${values.productPriceColor};">${values.productPrice}</div>
<a class="button no-underline no-border-radius" href="${values.productCTAAction.url}" target="${
values.productCTAAction.target
}" ${values.productCTAColor}; color: ${values.productCTATextColor};">${values.productCTA}</a>
</div>
</div>
${isViewer ? modalTemplate({ products: values.data.products }) : ""}
`;
};
const modalTemplate = function (data) {
return `
<div class="modal" id="product_library_modal">
<div class="modal-dialog modal-dialog-centered">
<div class="modal-content">
<div class="modal-header">
<h3 class="modal-title">Products Library</h3>
<button class="close" >×</button>
</div>
<div class="modal-body">
<div class="search-box">
<input type="text" class="form-control" placeholder="Start searching for products by their titles or ids" id="search-bar" 78%" />
<button id="search-btn" class="button" 20%">Search</button>
</div>
<div class="products-list">
${productItemsTemplate(data)}
</div>
</div>
<div class="modal-footer">
</div>
</div>
</div>
</div>
`;
};
const productItemsTemplate = _.template(`
<% _.forEach(products, function(item) { %>
>
>
<h 0.5rem 0; text-align: left;color:#000"><%= item.title %></h4>
<h 0.5rem 0; text-align: left;color:#000"><%= item.price %></h4>
</div>
<% }); %>
`);
unlayer.registerTool({
name: "product_tool",
label: "Product",
icon: "fa-tag",
supportedDisplayModes: ["web", "email"],
options: {
productContent: {
title: "Product Content",
position: 1,
options: {
productLibrary: {
label: "Add Product from store",
defaultValue: "",
widget: "product_library",
},
productImage: {
label: "Product Image",
defaultValue: {
url: "https://s3.amazonaws.com/unroll-images-production/projects/6553/1604576441796-339575",
},
widget: "image",
},
productBackground: {
label: "Product Background",
defaultValue: "#ffffff",
widget: "color_picker",
},
productBorderColor: {
label: "Product Border Color",
defaultValue: "#ffffff",
widget: "color_picker",
},
productBorderSize: {
label: "Product Border Size",
defaultValue: "1",
widget: "counter",
},
productTitleFontSize: {
label: "Product Title Font Size",
defaultValue: "16",
widget: "counter",
},
productTitleFont: {
label: "Product Title Font",
defaultValue: "Arial", // Tutaj ustawiamy wartość domyślną
widget: "dropdown",
data: {
options: [
{ label: "Global Font", value: "Global Font" },
{ label: "Andale Mono", value: "Andale Mono" },
{ label: "Arial", value: "Arial" },
{ label: "Arial Black", value: "Arial Black" },
{ label: "Book Antiqua", value: "Book Antiqua" },
{ label: "Cabin", value: "Cabin" },
{ label: "Comic Sans MS", value: "Comic Sans MS" },
{ label: "Courier New", value: "Courier New" },
{ label: "Crimson Text", value: "Crimson Text" },
{ label: "Georgia", value: "Georgia" },
{ label: "Helvetica", value: "Helvetica" },
{ label: "Impact", value: "Impact" },
{ label: "Jost", value: "Jost" },
{ label: "Lato", value: "Lato" },
{ label: "Lobster Two", value: "Lobster Two" },
{ label: "Montserrat", value: "Montserrat" },
{ label: "Old Standard TT", value: "Old Standard TT" },
{ label: "Open Sans", value: "Open Sans" },
{ label: "Pacifico", value: "Pacifico" },
{ label: "Playfair Display", value: "Playfair Display" },
{ label: "Poppins", value: "Poppins" },
{ label: "Raleway", value: "Raleway" },
{ label: "Rubik", value: "Rubik" },
{ label: "Source Sans Pro", value: "Source Sans Pro" },
{ label: "Symbol", value: "Symbol" },
{ label: "Syne", value: "Syne" },
{ label: "Tahoma", value: "Tahoma" },
{ label: "Terminal", value: "Terminal" },
{ label: "Times New Roman", value: "Times New Roman" },
{ label: "Trebuchet MS", value: "Trebuchet MS" },
{ label: "Verdana", value: "Verdana" },
],
},
},
productTitle: {
label: "Product Title",
defaultValue: "Product Title",
widget: "rich_text",
},
productDescriptionFontSize: {
label: "Product Description Font Size",
defaultValue: "14",
widget: "counter",
},
productDescriptionFont: {
label: "Product Description Font",
defaultValue: "Arial", // Tutaj ustawiamy wartość domyślną
widget: "dropdown",
data: {
options: [
{ label: "Global Font", value: "Global Font" },
{ label: "Andale Mono", value: "Andale Mono" },
{ label: "Arial", value: "Arial" },
{ label: "Arial Black", value: "Arial Black" },
{ label: "Book Antiqua", value: "Book Antiqua" },
{ label: "Cabin", value: "Cabin" },
{ label: "Comic Sans MS", value: "Comic Sans MS" },
{ label: "Courier New", value: "Courier New" },
{ label: "Crimson Text", value: "Crimson Text" },
{ label: "Georgia", value: "Georgia" },
{ label: "Helvetica", value: "Helvetica" },
{ label: "Impact", value: "Impact" },
{ label: "Jost", value: "Jost" },
{ label: "Lato", value: "Lato" },
{ label: "Lobster Two", value: "Lobster Two" },
{ label: "Montserrat", value: "Montserrat" },
{ label: "Old Standard TT", value: "Old Standard TT" },
{ label: "Open Sans", value: "Open Sans" },
{ label: "Pacifico", value: "Pacifico" },
{ label: "Playfair Display", value: "Playfair Display" },
{ label: "Poppins", value: "Poppins" },
{ label: "Raleway", value: "Raleway" },
{ label: "Rubik", value: "Rubik" },
{ label: "Source Sans Pro", value: "Source Sans Pro" },
{ label: "Symbol", value: "Symbol" },
{ label: "Syne", value: "Syne" },
{ label: "Tahoma", value: "Tahoma" },
{ label: "Terminal", value: "Terminal" },
{ label: "Times New Roman", value: "Times New Roman" },
{ label: "Trebuchet MS", value: "Trebuchet MS" },
{ label: "Verdana", value: "Verdana" },
],
},
},
productDescription: {
label: "Product Description",
defaultValue:
"Lorem ipsum is placeholder text commonly used in the graphic, print, and publishing industries for previewing layouts and visual mockups.",
widget: "rich_text",
},
productPrice: {
label: "Product Price",
defaultValue: "7.99",
widget: "text",
},
productPriceColor: {
label: "Product Price Color",
defaultValue: "#000000",
widget: "color_picker",
},
productPriceBackgroundColor: {
label: "Product Price Background",
defaultValue: "#ffffff",
widget: "color_picker",
},
productCTA: {
label: "Button Name",
defaultValue: "Buy Now",
widget: "text",
},
productCTAColor: {
label: "Button Color",
defaultValue: "#FFFF8C",
widget: "color_picker",
},
productCTATextColor: {
label: "Button Text Color",
defaultValue: "#000000",
widget: "color_picker",
},
productCTAPosition: {
label: "Horizontal Button",
defaultValue: true,
widget: "toggle",
},
productCTAAction: {
label: "Action Type",
defaultValue: {
name: "web",
values: {
href: "http://google.com",
target: "_blank",
},
},
widget: "link",
},
},
},
},
transformer: (values, source) => {
console.log(values, source);
let newValues = {
...values,
productTitle:
values.productLibrary.selected !== undefined ? values.productLibrary.selected.title : values.productTitle,
productPrice:
values.productLibrary.selected !== undefined ? values.productLibrary.selected.price : values.productPrice,
productDescription:
values.productLibrary.selected !== undefined
? values.productLibrary.selected.description
: values.productDescription,
productImage: {
url:
values.productLibrary.selected !== undefined ? values.productLibrary.selected.image : values.productImage.url,
},
productCTAAction: {
name: "web",
values: {
href:
values.productLibrary.selected !== undefined
? values.productLibrary.selected.url
: values.productCTAAction.values.href,
target: "_blank",
},
},
};
if (source.name === "productTitle") {
newValues.productTitle = values.productTitle;
}
if (source.name === "productDescription") {
newValues.productDescription = values.productDescription;
}
if (source.name === "productPrice") {
newValues.productPrice = values.productPrice;
}
if (source.name === "productImage") {
newValues.productImage = values.productImage;
}
if (source.name === "productCTAAction") {
newValues.productCTAAction = values.productCTAAction;
}
// Return updated values
return newValues;
},
values: {},
renderer: {
Viewer: unlayer.createViewer({
render(values) {
return toolTemplate(values, true);
},
}),
exporters: {
web: function (values) {
return toolTemplate(values);
},
email: function (values) {
return toolTemplate(values);
},
},
head: {
// As we need custom styling in export as well that's why we put those styles here
css: function (values) {
return `
.product-card-main {
position: relative;
min-width: 0;
word-wrap: break-word;
background-color: #fff;
background-clip: border-box;
border-radius: .25rem;
margin: auto;
text-align: center;
border-style:solid;
}
.product-card-body {
padding: 0 1rem 1rem;
text-align: left;
}
.product-card-body p {
margin: 0.7rem 0;
color:#000;
}
.product-card-body h3{
margin: 0.7rem 0;
color:#000;
}
.product-card-main img {
width: 100%;
object-fit: contain;
border-top-left-radius: 0.25rem;
border-top-right-radius: 0.25rem;
}
.product-card-main .product-footer.horizontal {
display: flex;
border-bottom-left-radius: 0.25rem;
border-bottom-right-radius: 0.25rem;
align-items: center;
font-weight: bold;
}
.product-card-main .product-footer.vertical {
border-bottom-left-radius: 0.25rem;
border-bottom-right-radius: 0.25rem;
font-weight: bold;
}
.product-card-main .product-footer.horizontal > div, .product-card-main .product-footer.horizontal > .button{
width: 50%;
}
.product-card-main .product-footer.vertical > div, .product-card-main .product-footer.vertical > .button{
width: calc(100% - 1.65rem)
}
.product-card-main .product-footer.horizontal > div {
border-bottom-left-radius: 0.25rem;
margin: auto;
}
.product-card-main .product-footer.vertical > div {
border-bottom-left-radius: 0.25rem;
padding: 0.75rem;
}
.product-card-main .product-footer.horizontal > .button {
border-bottom-right-radius: 0.25rem;
}
.button {
display: inline-block;
font-weight: 400;
color: #ffffff;
text-align: center;
vertical-align: middle;
background-color: transparent;
border: 1px solid transparent;
border-radius: 0.25rem;
padding: .75rem;
font-size: 1rem;
line-height: 1.5;
transition: color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;
background-color: rgb(0, 123, 255);
cursor: pointer;
}
.m-0 {
margin: 0px;
}
.no-underline {
text-decoration: none;
}
.no-border-radius {
border-radius: 0px;
}
`;
},
js: function (values) {},
},
},
});
const editorTemplate =
'<button id="addProduct" class="button" #FFFF8C;color:#000">Edit Product</button>';
const showModal = function () {
const modal = document.getElementById("product_library_modal");
modal.classList.add("show");
};
const hideModal = function () {
const modal = document.getElementById("product_library_modal");
modal.classList.remove("show");
};
const productLibrary = [];
unlayer.registerPropertyEditor({
name: "product_library",
layout: "bottom",
Widget: unlayer.createWidget({
// eslint-disable-next-line @typescript-eslint/no-unused-vars
render(value, updateValue, data) {
return editorTemplate;
},
mount(node, value, updateValue, data) {
var addButton = node.querySelector("#addProduct");
addButton.onclick = function () {
let o = {};
o.source = "edrone";
o.type = "feed";
o.message = "check feed";
window.parent.postMessage(o, "*");
if (data.products.length === 1 && data.products[0].title === "Product Title") {
const list = document.querySelector("#product_library_modal .products-list");
list[removed] =
"<p>To fully utilize this functionality, a product feed for your store is required.</p> <p>Click <a href='' id='toFeed' > here</a> to add your store’s product feed.</p>";
}
showModal();
setTimeout(() => {
// We are using event bubling to capture clicked item instead of registering click event on all product items.
var link = document.querySelector("#toFeed");
if (link !== null && link !== undefined) {
link.onclick = function (e) {
let o = {};
o.source = "edrone";
o.type = "feed";
o.message = "to feed";
window.parent.postMessage(o, "*");
};
}
var selectButton = document.querySelector(".products-list");
if (!selectButton) {
return;
}
selectButton.onclick = function (e) {
if (e.target.id === "product-item") {
let selectedProduct = {};
if (productLibrary.length === 0) {
selectedProduct = data.products.find((item) => item.id === parseInt(e.target.dataset.uuid));
} else {
selectedProduct = productLibrary.find((item) => item.id === parseInt(e.target.dataset.uuid));
}
updateValue({ selected: selectedProduct });
} else {
const parent = e.target.parentElement;
if (parent && parent.id !== "product-item") {
return;
}
let selectedProduct = {};
if (productLibrary.length === 0) {
selectedProduct = data.products.find((item) => item.id === parseInt(parent.dataset.uuid));
} else {
selectedProduct = productLibrary.find((item) => item.id === parseInt(parent.dataset.uuid));
}
updateValue({ selected: selectedProduct });
}
hideModal();
// This is a hack to close property editor right bar on selecting an item from products list.
var outerBody = document.querySelector("#u_body");
outerBody.click();
};
/* Register event listeners for search */
var searchBar = document.querySelector("#search-bar");
var searchButton = document.querySelector("#search-btn");
// eslint-disable-next-line @typescript-eslint/no-unused-vars
searchButton.onclick = async function (e) {
let o = {};
o.source = "edrone";
o.type = "search";
o.message = searchBar.value;
window.parent.postMessage(o, "*");
const list = document.querySelector("#product_library_modal .products-list");
list[removed] = "<p>Loading...</p>";
};
}, 200);
};
},
}),
});
window.addEventListener("message", (event) => {
let isProductsMessage = false;
try {
isProductsMessage = event.data.source === "edrone" && event.data.type === "products";
} catch (error) {
isProductsMessage = false;
}
if (isProductsMessage) {
productLibrary.length = 0;
productLibrary.push(...event.data.message);
const list = document.querySelector("#product_library_modal .products-list");
let filteredItem;
let productsListHtml;
if (list && productLibrary) {
filteredItem = productLibrary;
productsListHtml = productItemsTemplate({
products: productLibrary,
});
if (filteredItem.length > 0) {
list[removed] = productsListHtml;
} else {
list[removed] = "<p>No products found. Start searching for products by their titles or ids</p>";
}
}
}
});