From ca651532c6dcb9e1e7a5ef889de0e97a56d6caeb Mon Sep 17 00:00:00 2001 From: David Taylor Date: Thu, 21 Mar 2024 15:51:12 +0000 Subject: [PATCH] DEV: Convert d-modal to gjs (#26292) --- .../discourse/app/components/d-modal.gjs | 237 ++++++++++++++++++ .../discourse/app/components/d-modal.hbs | 102 -------- .../discourse/app/components/d-modal.js | 120 --------- 3 files changed, 237 insertions(+), 222 deletions(-) create mode 100644 app/assets/javascripts/discourse/app/components/d-modal.gjs delete mode 100644 app/assets/javascripts/discourse/app/components/d-modal.hbs delete mode 100644 app/assets/javascripts/discourse/app/components/d-modal.js diff --git a/app/assets/javascripts/discourse/app/components/d-modal.gjs b/app/assets/javascripts/discourse/app/components/d-modal.gjs new file mode 100644 index 00000000000..9a90a44cddd --- /dev/null +++ b/app/assets/javascripts/discourse/app/components/d-modal.gjs @@ -0,0 +1,237 @@ +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"; +import didInsert from "@ember/render-modifiers/modifiers/did-insert"; +import willDestroy from "@ember/render-modifiers/modifiers/will-destroy"; +import { service } from "@ember/service"; +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 trapTab from "discourse/modifiers/trap-tab"; + +export const CLOSE_INITIATED_BY_BUTTON = "initiatedByCloseButton"; +export const CLOSE_INITIATED_BY_ESC = "initiatedByESC"; +export const CLOSE_INITIATED_BY_CLICK_OUTSIDE = "initiatedByClickOut"; +export const CLOSE_INITIATED_BY_MODAL_SHOW = "initiatedByModalShow"; + +const FLASH_TYPES = ["success", "error", "warning", "info"]; + +export default class DModal extends Component { + @service modal; + @tracked wrapperElement; + + @action + setupListeners(element) { + document.documentElement.addEventListener( + "keydown", + this.handleDocumentKeydown + ); + this.wrapperElement = element; + } + + @action + cleanupListeners() { + document.documentElement.removeEventListener( + "keydown", + this.handleDocumentKeydown + ); + } + + get dismissable() { + if (!this.args.closeModal) { + return false; + } else if ("dismissable" in this.args) { + return this.args.dismissable; + } else { + return true; + } + } + + shouldTriggerClickOnEnter(event) { + if (this.args.submitOnEnter === false) { + return false; + } + + // skip when in a form or a textarea element + if ( + event.target.closest("form") || + document.activeElement?.nodeName === "TEXTAREA" + ) { + return false; + } + + return true; + } + + @action + handleWrapperClick(e) { + if (e.button !== 0) { + return; // Non-default mouse button + } + + if (!this.dismissable) { + return; + } + + return this.args.closeModal?.({ + initiatedBy: CLOSE_INITIATED_BY_CLICK_OUTSIDE, + }); + } + + @action + handleDocumentKeydown(event) { + if (this.args.hidden) { + return; + } + + if (event.key === "Escape" && this.dismissable) { + event.stopPropagation(); + this.args.closeModal({ initiatedBy: CLOSE_INITIATED_BY_ESC }); + } + + if (event.key === "Enter" && this.shouldTriggerClickOnEnter(event)) { + this.wrapperElement + .querySelector(".d-modal__footer .btn-primary") + ?.click(); + event.preventDefault(); + } + } + + @action + handleCloseButton() { + this.args.closeModal({ initiatedBy: CLOSE_INITIATED_BY_BUTTON }); + } + + @action + validateFlashType(type) { + if (type && !FLASH_TYPES.includes(type)) { + throw `@flashType must be one of ${FLASH_TYPES.join(", ")}`; + } + } + + // Could be optimised to remove classic component once RFC389 is implemented + // https://rfcs.emberjs.com/id/0389-dynamic-tag-names + @cached + get dynamicElement() { + const tagName = this.args.tagName || "div"; + if (!["div", "form"].includes(tagName)) { + throw `@tagName must be form or div`; + } + + return class WrapperComponent extends ClassicComponent { + tagName = tagName; + }; + } + + +} diff --git a/app/assets/javascripts/discourse/app/components/d-modal.hbs b/app/assets/javascripts/discourse/app/components/d-modal.hbs deleted file mode 100644 index ac5231f4a17..00000000000 --- a/app/assets/javascripts/discourse/app/components/d-modal.hbs +++ /dev/null @@ -1,102 +0,0 @@ -{{! template-lint-disable no-pointer-down-event-binding }} -{{! template-lint-disable no-invalid-interactive }} - - - - -
- {{yield to="aboveHeader"}} - - {{#if - (and - (not @hideHeader) - (or - this.dismissable - @title - (has-block "headerBelowTitle") - (has-block "headerAboveTitle") - ) - ) - }} -
- - {{yield to="headerAboveTitle"}} - - {{#if @title}} -
-

{{@title}}

- - {{#if @subtitle}} -

{{@subtitle}}

- {{/if}} - - {{yield to="belowModalTitle"}} -
- {{/if}} - {{yield to="headerBelowTitle"}} - - {{#if this.dismissable}} - - {{/if}} -
- {{/if}} - - {{yield to="belowHeader"}} - - {{this.validateFlashType @flashType}} - {{#if @flash}} - - {{/if}} - -
- {{#if (has-block "body")}} - {{yield to="body"}} - {{else}} - {{yield}} - {{/if}} -
- - {{#if (has-block "footer")}} - - {{/if}} - - {{yield to="belowFooter"}} -
-
- {{#unless @inline}} -
- {{/unless}} -
\ No newline at end of file diff --git a/app/assets/javascripts/discourse/app/components/d-modal.js b/app/assets/javascripts/discourse/app/components/d-modal.js deleted file mode 100644 index da0a8658991..00000000000 --- a/app/assets/javascripts/discourse/app/components/d-modal.js +++ /dev/null @@ -1,120 +0,0 @@ -import Component from "@glimmer/component"; -import { cached, tracked } from "@glimmer/tracking"; -import ClassicComponent from "@ember/component"; -import { action } from "@ember/object"; -import { service } from "@ember/service"; - -export const CLOSE_INITIATED_BY_BUTTON = "initiatedByCloseButton"; -export const CLOSE_INITIATED_BY_ESC = "initiatedByESC"; -export const CLOSE_INITIATED_BY_CLICK_OUTSIDE = "initiatedByClickOut"; -export const CLOSE_INITIATED_BY_MODAL_SHOW = "initiatedByModalShow"; - -const FLASH_TYPES = ["success", "error", "warning", "info"]; - -export default class DModal extends Component { - @service modal; - @tracked wrapperElement; - - @action - setupListeners(element) { - document.documentElement.addEventListener( - "keydown", - this.handleDocumentKeydown - ); - this.wrapperElement = element; - } - - @action - cleanupListeners() { - document.documentElement.removeEventListener( - "keydown", - this.handleDocumentKeydown - ); - } - - get dismissable() { - if (!this.args.closeModal) { - return false; - } else if ("dismissable" in this.args) { - return this.args.dismissable; - } else { - return true; - } - } - - shouldTriggerClickOnEnter(event) { - if (this.args.submitOnEnter === false) { - return false; - } - - // skip when in a form or a textarea element - if ( - event.target.closest("form") || - document.activeElement?.nodeName === "TEXTAREA" - ) { - return false; - } - - return true; - } - - @action - handleWrapperClick(e) { - if (e.button !== 0) { - return; // Non-default mouse button - } - - if (!this.dismissable) { - return; - } - - return this.args.closeModal?.({ - initiatedBy: CLOSE_INITIATED_BY_CLICK_OUTSIDE, - }); - } - - @action - handleDocumentKeydown(event) { - if (this.args.hidden) { - return; - } - - if (event.key === "Escape" && this.dismissable) { - event.stopPropagation(); - this.args.closeModal({ initiatedBy: CLOSE_INITIATED_BY_ESC }); - } - - if (event.key === "Enter" && this.shouldTriggerClickOnEnter(event)) { - this.wrapperElement - .querySelector(".d-modal__footer .btn-primary") - ?.click(); - event.preventDefault(); - } - } - - @action - handleCloseButton() { - this.args.closeModal({ initiatedBy: CLOSE_INITIATED_BY_BUTTON }); - } - - @action - validateFlashType(type) { - if (type && !FLASH_TYPES.includes(type)) { - throw `@flashType must be one of ${FLASH_TYPES.join(", ")}`; - } - } - - // Could be optimised to remove classic component once RFC389 is implemented - // https://rfcs.emberjs.com/id/0389-dynamic-tag-names - @cached - get dynamicElement() { - const tagName = this.args.tagName || "div"; - if (!["div", "form"].includes(tagName)) { - throw `@tagName must be form or div`; - } - - return class WrapperComponent extends ClassicComponent { - tagName = tagName; - }; - } -}