DEV: Introduce element helper (#27086)

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.
This commit is contained in:
David Taylor 2024-05-20 17:40:12 +01:00 committed by GitHub
parent 23b02a3824
commit fb9a43c282
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 75 additions and 12 deletions

View File

@ -1,6 +1,5 @@
import Component from "@glimmer/component"; import Component from "@glimmer/component";
import { cached, tracked } from "@glimmer/tracking"; import { cached, tracked } from "@glimmer/tracking";
import ClassicComponent from "@ember/component";
import { concat } from "@ember/helper"; import { concat } from "@ember/helper";
import { on } from "@ember/modifier"; import { on } from "@ember/modifier";
import { action } from "@ember/object"; 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 ConditionalInElement from "discourse/components/conditional-in-element";
import DButton from "discourse/components/d-button"; import DButton from "discourse/components/d-button";
import concatClass from "discourse/helpers/concat-class"; import concatClass from "discourse/helpers/concat-class";
import element from "discourse/helpers/element";
import { import {
disableBodyScroll, disableBodyScroll,
enableBodyScroll, enableBodyScroll,
@ -39,24 +39,24 @@ export default class DModal extends Component {
@tracked wrapperElement; @tracked wrapperElement;
@tracked animating = false; @tracked animating = false;
registerModalContainer = modifierFn((element) => { registerModalContainer = modifierFn((el) => {
this.modalContainer = element; this.modalContainer = el;
}); });
setupModalBody = modifierFn((element) => { setupModalBody = modifierFn((el) => {
if (!this.site.mobileView) { if (!this.site.mobileView) {
return; return;
} }
disableBodyScroll(element); disableBodyScroll(el);
return () => { return () => {
enableBodyScroll(element); enableBodyScroll(el);
}; };
}); });
@action @action
async setupModal(element) { async setupModal(el) {
document.documentElement.addEventListener( document.documentElement.addEventListener(
"keydown", "keydown",
this.handleDocumentKeydown this.handleDocumentKeydown
@ -70,7 +70,7 @@ export default class DModal extends Component {
if (this.site.mobileView) { if (this.site.mobileView) {
this.animating = true; this.animating = true;
await element.animate( await el.animate(
[{ transform: "translateY(100%)" }, { transform: "translateY(0)" }], [{ transform: "translateY(100%)" }, { transform: "translateY(0)" }],
{ {
duration: getMaxAnimationTimeMs(), duration: getMaxAnimationTimeMs(),
@ -82,7 +82,7 @@ export default class DModal extends Component {
this.animating = false; this.animating = false;
} }
this.wrapperElement = element; this.wrapperElement = el;
} }
@action @action
@ -226,9 +226,7 @@ export default class DModal extends Component {
throw `@tagName must be form or div`; throw `@tagName must be form or div`;
} }
return class WrapperComponent extends ClassicComponent { return element(tagName);
tagName = tagName;
};
} }
@bind @bind

View File

@ -0,0 +1,65 @@
import ClassicComponent from "@ember/component";
const empty = <template>
{{! template-lint-disable no-yield-only}}{{yield}}
</template>;
const shortcuts = {
div: <template><div ...attributes>{{yield}}</div></template>,
span: <template><span ...attributes>{{yield}}</span></template>,
form: <template><form ...attributes>{{yield}}</form></template>,
a: <template><a ...attributes>{{yield}}</a></template>,
button: <template><button ...attributes>{{yield}}</button></template>,
};
/**
* 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|}}
* <Wrapper class="pt-10 pb-10 ps-20 box-shadow" ...attributes>
* Content
* </Wrapper>
* {{/let}}
* ```
*
* Or in js:
*
* ```gjs
* class MyComponent {
* get wrapper(){
* return element(this.args.tagName);
* }
*
* <template>
* <this.wrapper>
* Content
* </this.wrapper>
* </template>
* }
* ```
*/
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 <template>
<ClassicComponent
@tagName={{tagName}}
...attributes
>{{yield}}</ClassicComponent>
</template>;
}
}