diff --git a/CRM/Core/Resources/Common.php b/CRM/Core/Resources/Common.php
index bb09a9cdff49..91f0a684fea8 100644
--- a/CRM/Core/Resources/Common.php
+++ b/CRM/Core/Resources/Common.php
@@ -206,6 +206,7 @@ protected static function coreResourceList($region) {
"js/crm.datepicker.js",
"js/crm.ajax.js",
"js/wysiwyg/crm.wysiwyg.js",
+ "js/crm.elements.js",
];
// Dynamic localization script
diff --git a/CRM/Core/xml/Menu/Misc.xml b/CRM/Core/xml/Menu/Misc.xml
index 3513292ddeff..1aaed44dbfc3 100644
--- a/CRM/Core/xml/Menu/Misc.xml
+++ b/CRM/Core/xml/Menu/Misc.xml
@@ -284,4 +284,9 @@
CRM_Contact_Form_Task_Delete
access CiviCRM
+ -
+ civicrm/elements
+ Civi\Core\Elements::get
+ *always allow*
+
diff --git a/CRM/Custom/Form/Field.php b/CRM/Custom/Form/Field.php
index 6b683875370b..ee8107aecc51 100644
--- a/CRM/Custom/Form/Field.php
+++ b/CRM/Custom/Form/Field.php
@@ -110,8 +110,8 @@ public function preProcess() {
CRM_Core_Error::statusBounce("You cannot add or edit fields in a reserved custom field-set.");
}
- // Add crm-options-repeat web component. FIXME: need an autoloader for web components.
- \Civi::resources()->addScriptFile('civicrm', 'js/CrmOptionsRepeat.js');
+ // Add crm-options-repeat web component. FIXME: autoload
+ \Civi::resources()->addScript('CRM.loadElement("civi-options-repeat")', ['weight' => -100]);
if ($this->_gid) {
$url = CRM_Utils_System::url('civicrm/admin/custom/group/field',
diff --git a/Civi/Core/Elements.php b/Civi/Core/Elements.php
new file mode 100644
index 000000000000..33f1f8564f1c
--- /dev/null
+++ b/Civi/Core/Elements.php
@@ -0,0 +1,56 @@
+getQueryParams();
+ $element = explode('/', $query['q'])[2];
+ $locale = $query['locale'] ?? NULL;
+
+ // TODO cache control?
+ $headers = [];
+
+// load css/js alongside the template?
+// inserting
+//
+// HTML;
+
+ $body = self::renderTemplate($element, $locale);
+
+ return new Response(200, $headers, $body);
+
+ }
+
+ protected static function renderTemplate(string $element, ?string $locale = NULL): string {
+ if ($locale) {
+ try {
+ \CRM_Core_I18n::singleton()->setLocale($locale);
+ }
+ catch (\Throwable $e) {
+ // just use default
+ }
+ }
+ try {
+ return \CRM_Core_Smarty::singleton()->fetch("elements/{$element}.tpl");
+ }
+ catch (\Throwable $e) {
+ return '';
+ }
+ }
+
+}
diff --git a/js/CrmOptionsRepeat.js b/elements/civi-options-repeat.js
similarity index 69%
rename from js/CrmOptionsRepeat.js
rename to elements/civi-options-repeat.js
index c2be6bcbbd60..d4b075e67033 100644
--- a/js/CrmOptionsRepeat.js
+++ b/elements/civi-options-repeat.js
@@ -1,62 +1,6 @@
class CrmOptionsRepeat extends HTMLElement {
- static template = `
-
- `;
+ static tag = 'civi-options-repeat';
constructor() {
super();
@@ -73,12 +17,16 @@ class CrmOptionsRepeat extends HTMLElement {
}
}
+ renderTemplate() {
+ const template = document.querySelector('template#' + this.constructor.tag);
+ this.append(document.importNode(template.content, true));
+ }
init() {
// TODO: use this element's value attribute
this.hiddenInput = this.previousElementSibling;
- this.innerHTML = CrmOptionsRepeat.template;
+ this.renderTemplate();
this.table = this.querySelector('table tbody');
diff --git a/js/crm.elements.js b/js/crm.elements.js
new file mode 100644
index 000000000000..2aea76e5ce55
--- /dev/null
+++ b/js/crm.elements.js
@@ -0,0 +1,18 @@
+CRM = CRM || {};
+
+// TODO: merge into elements mixin autoloader
+CRM.loadElement = (tagName) => {
+
+ // load template first, so the JS doesn't have to wait for it
+ // TODO: skip if no template
+ fetch(CRM.url(`civicrm/elements/${tagName}`))
+ .then((response) => response.text())
+ .then((content) => {
+ template = document.createElement('template');
+ template.id = tagName;
+ template.innerHTML= content;
+ document.body.append(template);
+ })
+ // now load the custom element definition
+ .then(() => import(`${CRM.resourceUrls.civicrm}/elements/${tagName}.js`));
+}
\ No newline at end of file
diff --git a/templates/elements/civi-options-repeat.tpl b/templates/elements/civi-options-repeat.tpl
new file mode 100644
index 000000000000..8e9d0d382e83
--- /dev/null
+++ b/templates/elements/civi-options-repeat.tpl
@@ -0,0 +1,55 @@
+
\ No newline at end of file