diff --git a/frappe/public/js/frappe/widgets/custom_block_widget.js b/frappe/public/js/frappe/widgets/custom_block_widget.js new file mode 100644 index 0000000000..c64c2340a3 --- /dev/null +++ b/frappe/public/js/frappe/widgets/custom_block_widget.js @@ -0,0 +1,78 @@ +import Widget from "./base_widget.js"; + +export default class CustomBlockWidget extends Widget { + constructor(opts) { + opts.shadow = true; + super(opts); + } + + get_config() { + return { + custom_block_name: this.custom_block_name, + label: this.custom_block_name, + }; + } + + refresh() { + this.set_body(); + this.make_custom_block(); + } + + set_body() { + this.widget.addClass("custom-block-widget-box"); + this.widget.addClass("full-width"); + } + + async make_custom_block() { + await this.get_custom_block_data(); + this.body.empty(); + + this.random_id = "custom-block-" + frappe.utils.get_random(5).toLowerCase(); + + let me = this; + + class CustomBlock extends HTMLElement { + constructor() { + super(); + + // html + let div = document.createElement("div"); + div.innerHTML = frappe.dom.remove_script_and_style(me.custom_block_doc.html); + + // css + let style = document.createElement("style"); + style.textContent = me.custom_block_doc.style; + + // js + let script = document.createElement("script"); + script.textContent = ` + (function() { + let cname = ${JSON.stringify(me.random_id)}; + let root_element = document.querySelector(cname).shadowRoot; + ${me.custom_block_doc.script} + })(); + `; + + this.attachShadow({ mode: "open" }); + this.shadowRoot?.appendChild(div); + this.shadowRoot?.appendChild(style); + this.shadowRoot?.appendChild(script); + } + } + + if (!customElements.get(this.random_id)) { + customElements.define(this.random_id, CustomBlock); + } + + this.body.append(`<${this.random_id}>`); + } + + async get_custom_block_data() { + this.label = this.custom_block_name; + let custom_block_doc = await frappe.model.with_doc( + "Custom HTML Block", + this.custom_block_name + ); + this.custom_block_doc = custom_block_doc ? custom_block_doc : ""; + } +} diff --git a/frappe/public/js/frappe/widgets/widget_dialog.js b/frappe/public/js/frappe/widgets/widget_dialog.js index db402211df..ba5270c27a 100644 --- a/frappe/public/js/frappe/widgets/widget_dialog.js +++ b/frappe/public/js/frappe/widgets/widget_dialog.js @@ -701,6 +701,24 @@ class NumberCardDialog extends WidgetDialog { } } +class CustomBlockDialog extends WidgetDialog { + constructor(opts) { + super(opts); + } + + get_fields() { + return [ + { + fieldtype: "Link", + fieldname: "custom_block_name", + label: "Custom Block Name", + options: "Custom HTML Block", + reqd: 1, + }, + ]; + } +} + export default function get_dialog_constructor(type) { const widget_map = { chart: ChartDialog, @@ -709,6 +727,7 @@ export default function get_dialog_constructor(type) { onboarding: OnboardingDialog, quick_list: QuickListDialog, number_card: NumberCardDialog, + custom_block: CustomBlockDialog, }; return widget_map[type] || WidgetDialog; diff --git a/frappe/public/js/frappe/widgets/widget_group.js b/frappe/public/js/frappe/widgets/widget_group.js index 17330071ba..26c748d1b9 100644 --- a/frappe/public/js/frappe/widgets/widget_group.js +++ b/frappe/public/js/frappe/widgets/widget_group.js @@ -6,6 +6,7 @@ import OnboardingWidget from "../widgets/onboarding_widget"; import NewWidget from "../widgets/new_widget"; import NumberCardWidget from "../widgets/number_card_widget"; import QuickListWidget from "../widgets/quick_list_widget"; +import CustomBlock from "../widgets/custom_block_widget"; frappe.provide("frappe.widget"); @@ -17,6 +18,7 @@ frappe.widget.widget_factory = { onboarding: OnboardingWidget, number_card: NumberCardWidget, quick_list: QuickListWidget, + custom_block: CustomBlock, }; frappe.widget.make_widget = (opts) => {