Facebook
From Adrian, 5 Months ago, written in JavaScript.
Embed
Download Paste or View Raw
Hits: 120
  1. const toolTemplate = function (values, isViewer = false) {
  2.   return `<div class="product-card-main"  ${values.productBackground};border-color:${
  3.     values.productBorderColor
  4.   }; border-width: ${values.productBorderSize}px;">
  5.  <img src="${values.productImage.url}" />
  6.  <div class="product-card-body"
  7.    values.productTitleFontSize
  8.  }px">
  9.     <h3 class="m-0">${values.productTitle}</h3>
  10.     <div class="description"  !important;font-size:${
  11.     values.productDescriptionFontSize
  12.   }px">${values.productDescription}</div>
  13.  </div>
  14.  <div class="product-footer ${
  15.      values.productCTAPositi true ? "horizontal" : "vertical"
  16.    }"  ${values.productPriceBackgroundColor};">
  17.     <div  ${values.productPriceColor};">${values.productPrice}</div>
  18.    <a class="button no-underline no-border-radius" href="${values.productCTAAction.url}" target="${
  19.     values.productCTAAction.target
  20.    }"  ${values.productCTAColor}; color: ${values.productCTATextColor};">${values.productCTA}</a>
  21.   </div>
  22. </div>
  23. ${isViewer ? modalTemplate({ products: values.data.products }) : ""}
  24. `;
  25. };
  26.  
  27. const modalTemplate = function (data) {
  28.   return `
  29.   <div class="modal" id="product_library_modal">
  30.     <div class="modal-dialog modal-dialog-centered">
  31.       <div class="modal-content">
  32.         <div class="modal-header">
  33.           <h3 class="modal-title">Products Library</h3>
  34.           <button class="close" >&times;</button>
  35.         </div>
  36.         <div class="modal-body">
  37.           <div class="search-box">
  38.             &lt;input type="text" class="form-control" placeholder="Start searching for products by their titles or ids" id="search-bar"  78%" /&gt;
  39.            <button id="search-btn" class="button"  20%">Search</button>
  40.           </div>
  41.           <div class="products-list">
  42.             ${productItemsTemplate(data)}
  43.           </div>
  44.         </div>
  45.         <div class="modal-footer">
  46.         </div>
  47.       </div>
  48.     </div>
  49.   </div>
  50. `;
  51. };
  52.  
  53. const productItemsTemplate = _.template(`
  54. <% _.forEach(products, function(item) { %>
  55.   >
  56.   >
  57.     <h   0.5rem 0; text-align: left;color:#000"><%= item.title %></h4>
  58.    <h   0.5rem 0; text-align: left;color:#000"><%= item.price %></h4>
  59.   </div>
  60. <% }); %>
  61. `);
  62.  
  63. unlayer.registerTool({
  64.   name: "product_tool",
  65.   label: "Product",
  66.   icon: "fa-tag",
  67.   supportedDisplayModes: ["web", "email"],
  68.   options: {
  69.     productContent: {
  70.       title: "Product Content",
  71.       position: 1,
  72.       options: {
  73.         productLibrary: {
  74.           label: "Add Product from store",
  75.           defaultValue: "",
  76.           widget: "product_library",
  77.         },
  78.         productImage: {
  79.           label: "Product Image",
  80.           defaultValue: {
  81.             url: "https://s3.amazonaws.com/unroll-images-production/projects/6553/1604576441796-339575",
  82.           },
  83.           widget: "image",
  84.         },
  85.         productBackground: {
  86.           label: "Product Background",
  87.           defaultValue: "#ffffff",
  88.           widget: "color_picker",
  89.         },
  90.         productBorderColor: {
  91.           label: "Product Border Color",
  92.           defaultValue: "#ffffff",
  93.           widget: "color_picker",
  94.         },
  95.         productBorderSize: {
  96.           label: "Product Border Size",
  97.           defaultValue: "1",
  98.           widget: "counter",
  99.         },
  100.         productTitleFontSize: {
  101.           label: "Product Title Font Size",
  102.           defaultValue: "16",
  103.           widget: "counter",
  104.         },
  105.         productTitleFont: {
  106.           label: "Product Title Font",
  107.           defaultValue: "Arial", // Tutaj ustawiamy wartość domyślną
  108.           widget: "dropdown",
  109.           data: {
  110.             options: [
  111.               { label: "Global Font", value: "Global Font" },
  112.               { label: "Andale Mono", value: "Andale Mono" },
  113.               { label: "Arial", value: "Arial" },
  114.               { label: "Arial Black", value: "Arial Black" },
  115.               { label: "Book Antiqua", value: "Book Antiqua" },
  116.               { label: "Cabin", value: "Cabin" },
  117.               { label: "Comic Sans MS", value: "Comic Sans MS" },
  118.               { label: "Courier New", value: "Courier New" },
  119.               { label: "Crimson Text", value: "Crimson Text" },
  120.               { label: "Georgia", value: "Georgia" },
  121.               { label: "Helvetica", value: "Helvetica" },
  122.               { label: "Impact", value: "Impact" },
  123.               { label: "Jost", value: "Jost" },
  124.               { label: "Lato", value: "Lato" },
  125.               { label: "Lobster Two", value: "Lobster Two" },
  126.               { label: "Montserrat", value: "Montserrat" },
  127.               { label: "Old Standard TT", value: "Old Standard TT" },
  128.               { label: "Open Sans", value: "Open Sans" },
  129.               { label: "Pacifico", value: "Pacifico" },
  130.               { label: "Playfair Display", value: "Playfair Display" },
  131.               { label: "Poppins", value: "Poppins" },
  132.               { label: "Raleway", value: "Raleway" },
  133.               { label: "Rubik", value: "Rubik" },
  134.               { label: "Source Sans Pro", value: "Source Sans Pro" },
  135.               { label: "Symbol", value: "Symbol" },
  136.               { label: "Syne", value: "Syne" },
  137.               { label: "Tahoma", value: "Tahoma" },
  138.               { label: "Terminal", value: "Terminal" },
  139.               { label: "Times New Roman", value: "Times New Roman" },
  140.               { label: "Trebuchet MS", value: "Trebuchet MS" },
  141.               { label: "Verdana", value: "Verdana" },
  142.             ],
  143.           },
  144.         },
  145.         productTitle: {
  146.           label: "Product Title",
  147.           defaultValue: "Product Title",
  148.           widget: "rich_text",
  149.         },
  150.         productDescriptionFontSize: {
  151.           label: "Product Description Font Size",
  152.           defaultValue: "14",
  153.           widget: "counter",
  154.         },
  155.         productDescriptionFont: {
  156.           label: "Product Description Font",
  157.           defaultValue: "Arial", // Tutaj ustawiamy wartość domyślną
  158.           widget: "dropdown",
  159.           data: {
  160.             options: [
  161.               { label: "Global Font", value: "Global Font" },
  162.               { label: "Andale Mono", value: "Andale Mono" },
  163.               { label: "Arial", value: "Arial" },
  164.               { label: "Arial Black", value: "Arial Black" },
  165.               { label: "Book Antiqua", value: "Book Antiqua" },
  166.               { label: "Cabin", value: "Cabin" },
  167.               { label: "Comic Sans MS", value: "Comic Sans MS" },
  168.               { label: "Courier New", value: "Courier New" },
  169.               { label: "Crimson Text", value: "Crimson Text" },
  170.               { label: "Georgia", value: "Georgia" },
  171.               { label: "Helvetica", value: "Helvetica" },
  172.               { label: "Impact", value: "Impact" },
  173.               { label: "Jost", value: "Jost" },
  174.               { label: "Lato", value: "Lato" },
  175.               { label: "Lobster Two", value: "Lobster Two" },
  176.               { label: "Montserrat", value: "Montserrat" },
  177.               { label: "Old Standard TT", value: "Old Standard TT" },
  178.               { label: "Open Sans", value: "Open Sans" },
  179.               { label: "Pacifico", value: "Pacifico" },
  180.               { label: "Playfair Display", value: "Playfair Display" },
  181.               { label: "Poppins", value: "Poppins" },
  182.               { label: "Raleway", value: "Raleway" },
  183.               { label: "Rubik", value: "Rubik" },
  184.               { label: "Source Sans Pro", value: "Source Sans Pro" },
  185.               { label: "Symbol", value: "Symbol" },
  186.               { label: "Syne", value: "Syne" },
  187.               { label: "Tahoma", value: "Tahoma" },
  188.               { label: "Terminal", value: "Terminal" },
  189.               { label: "Times New Roman", value: "Times New Roman" },
  190.               { label: "Trebuchet MS", value: "Trebuchet MS" },
  191.               { label: "Verdana", value: "Verdana" },
  192.             ],
  193.           },
  194.         },
  195.         productDescription: {
  196.           label: "Product Description",
  197.           defaultValue:
  198.             "Lorem ipsum is placeholder text commonly used in the graphic, print, and publishing industries for previewing layouts and visual mockups.",
  199.           widget: "rich_text",
  200.         },
  201.         productPrice: {
  202.           label: "Product Price",
  203.           defaultValue: "7.99",
  204.           widget: "text",
  205.         },
  206.         productPriceColor: {
  207.           label: "Product Price Color",
  208.           defaultValue: "#000000",
  209.           widget: "color_picker",
  210.         },
  211.         productPriceBackgroundColor: {
  212.           label: "Product Price Background",
  213.           defaultValue: "#ffffff",
  214.           widget: "color_picker",
  215.         },
  216.         productCTA: {
  217.           label: "Button Name",
  218.           defaultValue: "Buy Now",
  219.           widget: "text",
  220.         },
  221.         productCTAColor: {
  222.           label: "Button Color",
  223.           defaultValue: "#FFFF8C",
  224.           widget: "color_picker",
  225.         },
  226.         productCTATextColor: {
  227.           label: "Button Text Color",
  228.           defaultValue: "#000000",
  229.           widget: "color_picker",
  230.         },
  231.         productCTAPosition: {
  232.           label: "Horizontal Button",
  233.           defaultValue: true,
  234.           widget: "toggle",
  235.         },
  236.         productCTAAction: {
  237.           label: "Action Type",
  238.           defaultValue: {
  239.             name: "web",
  240.             values: {
  241.               href: "http://google.com",
  242.               target: "_blank",
  243.             },
  244.           },
  245.           widget: "link",
  246.         },
  247.       },
  248.     },
  249.   },
  250.   transformer: (values, source) => {
  251.     console.log(values, source);
  252.     let newValues = {
  253.       ...values,
  254.       productTitle:
  255.         values.productLibrary.selected !== undefined ? values.productLibrary.selected.title : values.productTitle,
  256.       productPrice:
  257.         values.productLibrary.selected !== undefined ? values.productLibrary.selected.price : values.productPrice,
  258.       productDescription:
  259.         values.productLibrary.selected !== undefined
  260.           ? values.productLibrary.selected.description
  261.           : values.productDescription,
  262.       productImage: {
  263.         url:
  264.           values.productLibrary.selected !== undefined ? values.productLibrary.selected.image : values.productImage.url,
  265.       },
  266.       productCTAAction: {
  267.         name: "web",
  268.         values: {
  269.           href:
  270.             values.productLibrary.selected !== undefined
  271.               ? values.productLibrary.selected.url
  272.               : values.productCTAAction.values.href,
  273.           target: "_blank",
  274.         },
  275.       },
  276.     };
  277.  
  278.     if (source.name === "productTitle") {
  279.       newValues.productTitle = values.productTitle;
  280.     }
  281.  
  282.     if (source.name === "productDescription") {
  283.       newValues.productDescription = values.productDescription;
  284.     }
  285.  
  286.     if (source.name === "productPrice") {
  287.       newValues.productPrice = values.productPrice;
  288.     }
  289.  
  290.     if (source.name === "productImage") {
  291.       newValues.productImage = values.productImage;
  292.     }
  293.  
  294.     if (source.name === "productCTAAction") {
  295.       newValues.productCTAAction = values.productCTAAction;
  296.     }
  297.  
  298.     // Return updated values
  299.     return newValues;
  300.   },
  301.   values: {},
  302.   renderer: {
  303.     Viewer: unlayer.createViewer({
  304.       render(values) {
  305.         return toolTemplate(values, true);
  306.       },
  307.     }),
  308.     exporters: {
  309.       web: function (values) {
  310.         return toolTemplate(values);
  311.       },
  312.       email: function (values) {
  313.         return toolTemplate(values);
  314.       },
  315.     },
  316.     head: {
  317.       // As we need custom styling in export as well that's why we put those styles here
  318.       css: function (values) {
  319.         return `      
  320.         .product-card-main {
  321.           position: relative;
  322.           min-width: 0;
  323.           word-wrap: break-word;
  324.           background-color: #fff;
  325.           background-clip: border-box;
  326.           border-radius: .25rem;
  327.           margin: auto;
  328.           text-align: center;
  329.           border-style:solid;
  330.         }
  331.  
  332.         .product-card-body {
  333.           padding: 0 1rem 1rem;
  334.           text-align: left;
  335.         }
  336.  
  337.         .product-card-body p {
  338.           margin: 0.7rem 0;
  339.           color:#000;
  340.         }
  341.         .product-card-body h3{
  342.           margin: 0.7rem 0;
  343.           color:#000;
  344.         }
  345.  
  346.         .product-card-main img {
  347.           width: 100%;
  348.           object-fit: contain;
  349.           border-top-left-radius: 0.25rem;
  350.           border-top-right-radius: 0.25rem;
  351.         }
  352.  
  353.         .product-card-main .product-footer.horizontal {
  354.           display: flex;
  355.           border-bottom-left-radius: 0.25rem;
  356.           border-bottom-right-radius: 0.25rem;
  357.           align-items: center;
  358.           font-weight: bold;
  359.         }
  360.  
  361.         .product-card-main .product-footer.vertical {
  362.           border-bottom-left-radius: 0.25rem;
  363.           border-bottom-right-radius: 0.25rem;
  364.           font-weight: bold;
  365.         }
  366.  
  367.         .product-card-main .product-footer.horizontal > div, .product-card-main .product-footer.horizontal > .button{
  368.           width: 50%;
  369.         }
  370.  
  371.         .product-card-main .product-footer.vertical > div, .product-card-main .product-footer.vertical > .button{
  372.           width: calc(100% - 1.65rem)
  373.         }
  374.  
  375.         .product-card-main .product-footer.horizontal > div {
  376.           border-bottom-left-radius: 0.25rem;
  377.           margin: auto;
  378.         }
  379.  
  380.         .product-card-main .product-footer.vertical > div {
  381.           border-bottom-left-radius: 0.25rem;
  382.           padding: 0.75rem;
  383.         }
  384.  
  385.         .product-card-main .product-footer.horizontal > .button {
  386.           border-bottom-right-radius: 0.25rem;
  387.         }
  388.  
  389.         .button {
  390.           display: inline-block;
  391.           font-weight: 400;
  392.           color: #ffffff;
  393.           text-align: center;
  394.           vertical-align: middle;
  395.           background-color: transparent;
  396.           border: 1px solid transparent;
  397.           border-radius: 0.25rem;
  398.           padding: .75rem;
  399.           font-size: 1rem;
  400.           line-height: 1.5;
  401.           transition: color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;
  402.           background-color: rgb(0, 123, 255);
  403.           cursor: pointer;
  404.         }
  405.  
  406.         .m-0 {
  407.           margin: 0px;
  408.         }
  409.  
  410.         .no-underline {
  411.           text-decoration: none;
  412.         }
  413.  
  414.         .no-border-radius {
  415.           border-radius: 0px;
  416.         }
  417.         `;
  418.       },
  419.       js: function (values) {},
  420.     },
  421.   },
  422. });
  423.  
  424. const editorTemplate =
  425.   '<button id="addProduct" class="button" #FFFF8C;color:#000">Edit Product</button>';
  426.  
  427. const showModal = function () {
  428.   const modal = document.getElementById("product_library_modal");
  429.   modal.classList.add("show");
  430. };
  431.  
  432. const hideModal = function () {
  433.   const modal = document.getElementById("product_library_modal");
  434.   modal.classList.remove("show");
  435. };
  436.  
  437. const productLibrary = [];
  438.  
  439. unlayer.registerPropertyEditor({
  440.   name: "product_library",
  441.   layout: "bottom",
  442.   Widget: unlayer.createWidget({
  443.     // eslint-disable-next-line @typescript-eslint/no-unused-vars
  444.     render(value, updateValue, data) {
  445.       return editorTemplate;
  446.     },
  447.     mount(node, value, updateValue, data) {
  448.       var addButton = node.querySelector("#addProduct");
  449.       addButton.onclick = function () {
  450.         let o = {};
  451.         o.source = "edrone";
  452.         o.type = "feed";
  453.         o.message = "check feed";
  454.         window.parent.postMessage(o, "*");
  455.         if (data.products.length === 1 && data.products[0].title === "Product Title") {
  456.           const list = document.querySelector("#product_library_modal .products-list");
  457.           list[removed] =
  458.             "<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>";
  459.         }
  460.         showModal();
  461.         setTimeout(() => {
  462.           // We are using event bubling to capture clicked item instead of registering click event on all product items.
  463.           var link = document.querySelector("#toFeed");
  464.           if (link !== null && link !== undefined) {
  465.             link.onclick = function (e) {
  466.               let o = {};
  467.               o.source = "edrone";
  468.               o.type = "feed";
  469.               o.message = "to feed";
  470.               window.parent.postMessage(o, "*");
  471.             };
  472.           }
  473.           var selectButton = document.querySelector(".products-list");
  474.           if (!selectButton) {
  475.             return;
  476.           }
  477.           selectButton.onclick = function (e) {
  478.             if (e.target.id === "product-item") {
  479.               let selectedProduct = {};
  480.               if (productLibrary.length === 0) {
  481.                 selectedProduct = data.products.find((item) => item.id === parseInt(e.target.dataset.uuid));
  482.               } else {
  483.                 selectedProduct = productLibrary.find((item) => item.id === parseInt(e.target.dataset.uuid));
  484.               }
  485.               updateValue({ selected: selectedProduct });
  486.             } else {
  487.               const parent = e.target.parentElement;
  488.               if (parent && parent.id !== "product-item") {
  489.                 return;
  490.               }
  491.               let selectedProduct = {};
  492.               if (productLibrary.length === 0) {
  493.                 selectedProduct = data.products.find((item) => item.id === parseInt(parent.dataset.uuid));
  494.               } else {
  495.                 selectedProduct = productLibrary.find((item) => item.id === parseInt(parent.dataset.uuid));
  496.               }
  497.               updateValue({ selected: selectedProduct });
  498.             }
  499.             hideModal();
  500.             // This is a hack to close property editor right bar on selecting an item from products list.
  501.             var outerBody = document.querySelector("#u_body");
  502.             outerBody.click();
  503.           };
  504.           /* Register event listeners for search */
  505.           var searchBar = document.querySelector("#search-bar");
  506.           var searchButton = document.querySelector("#search-btn");
  507.  
  508.           // eslint-disable-next-line @typescript-eslint/no-unused-vars
  509.           searchButton.onclick = async function (e) {
  510.             let o = {};
  511.             o.source = "edrone";
  512.             o.type = "search";
  513.             o.message = searchBar.value;
  514.             window.parent.postMessage(o, "*");
  515.             const list = document.querySelector("#product_library_modal .products-list");
  516.             list[removed] = "<p>Loading...</p>";
  517.           };
  518.         }, 200);
  519.       };
  520.     },
  521.   }),
  522. });
  523.  
  524. window.addEventListener("message", (event) => {
  525.   let isProductsMessage = false;
  526.   try {
  527.     isProductsMessage = event.data.source === "edrone" && event.data.type === "products";
  528.   } catch (error) {
  529.     isProductsMessage = false;
  530.   }
  531.   if (isProductsMessage) {
  532.     productLibrary.length = 0;
  533.     productLibrary.push(...event.data.message);
  534.     const list = document.querySelector("#product_library_modal .products-list");
  535.     let filteredItem;
  536.     let productsListHtml;
  537.     if (list && productLibrary) {
  538.       filteredItem = productLibrary;
  539.       productsListHtml = productItemsTemplate({
  540.         products: productLibrary,
  541.       });
  542.       if (filteredItem.length > 0) {
  543.         list[removed] = productsListHtml;
  544.       } else {
  545.         list[removed] = "<p>No products found. Start searching for products by their titles or ids</p>";
  546.       }
  547.     }
  548.   }
  549. });
  550.