diff --git a/app/assets/javascripts/discourse/app/components/d-modal.gjs b/app/assets/javascripts/discourse/app/components/d-modal.gjs
index 6bb6b5edabe..7ca44e041a7 100644
--- a/app/assets/javascripts/discourse/app/components/d-modal.gjs
+++ b/app/assets/javascripts/discourse/app/components/d-modal.gjs
@@ -1,6 +1,5 @@
import Component from "@glimmer/component";
import { cached, tracked } from "@glimmer/tracking";
-import ClassicComponent from "@ember/component";
import { concat } from "@ember/helper";
import { on } from "@ember/modifier";
import { action } from "@ember/object";
@@ -12,6 +11,7 @@ import { and, not, or } from "truth-helpers";
import ConditionalInElement from "discourse/components/conditional-in-element";
import DButton from "discourse/components/d-button";
import concatClass from "discourse/helpers/concat-class";
+import element from "discourse/helpers/element";
import {
disableBodyScroll,
enableBodyScroll,
@@ -39,24 +39,24 @@ export default class DModal extends Component {
@tracked wrapperElement;
@tracked animating = false;
- registerModalContainer = modifierFn((element) => {
- this.modalContainer = element;
+ registerModalContainer = modifierFn((el) => {
+ this.modalContainer = el;
});
- setupModalBody = modifierFn((element) => {
+ setupModalBody = modifierFn((el) => {
if (!this.site.mobileView) {
return;
}
- disableBodyScroll(element);
+ disableBodyScroll(el);
return () => {
- enableBodyScroll(element);
+ enableBodyScroll(el);
};
});
@action
- async setupModal(element) {
+ async setupModal(el) {
document.documentElement.addEventListener(
"keydown",
this.handleDocumentKeydown
@@ -70,7 +70,7 @@ export default class DModal extends Component {
if (this.site.mobileView) {
this.animating = true;
- await element.animate(
+ await el.animate(
[{ transform: "translateY(100%)" }, { transform: "translateY(0)" }],
{
duration: getMaxAnimationTimeMs(),
@@ -82,7 +82,7 @@ export default class DModal extends Component {
this.animating = false;
}
- this.wrapperElement = element;
+ this.wrapperElement = el;
}
@action
@@ -226,9 +226,7 @@ export default class DModal extends Component {
throw `@tagName must be form or div`;
}
- return class WrapperComponent extends ClassicComponent {
- tagName = tagName;
- };
+ return element(tagName);
}
@bind
diff --git a/app/assets/javascripts/discourse/app/helpers/element.gjs b/app/assets/javascripts/discourse/app/helpers/element.gjs
new file mode 100644
index 00000000000..af6142db0df
--- /dev/null
+++ b/app/assets/javascripts/discourse/app/helpers/element.gjs
@@ -0,0 +1,65 @@
+import ClassicComponent from "@ember/component";
+
+const empty =
+ {{! template-lint-disable no-yield-only}}{{yield}}
+;
+const shortcuts = {
+ div: {{yield}}
,
+ span: {{yield}},
+ form: ,
+ a: {{yield}},
+ button: ,
+};
+
+/**
+ * Returns a wrapper component with the given tagname, or an empty wrapper for an empty string.
+ * Similar to the reference implementation of RFC389, with higher-performance shortcuts for common elements.
+ *
+ * Can be used directly in a template:
+ *
+ * ```hbs
+ * {{#let (element @tagName) as |Wrapper|}}
+ *
+ * Content
+ *
+ * {{/let}}
+ * ```
+ *
+ * Or in js:
+ *
+ * ```gjs
+ * class MyComponent {
+ * get wrapper(){
+ * return element(this.args.tagName);
+ * }
+ *
+ *
+ *
+ * Content
+ *
+ *
+ * }
+ * ```
+ */
+export default function element(tagName) {
+ if (typeof tagName !== "string") {
+ throw new Error(
+ `element helper only accepts string literals, you passed ${tagName}`
+ );
+ }
+
+ if (tagName === null || tagName === undefined) {
+ return null;
+ } else if (tagName === "") {
+ return empty;
+ } else if (shortcuts[tagName]) {
+ return shortcuts[tagName];
+ } else {
+ return
+ {{yield}}
+ ;
+ }
+}