UX: Better composer hyperlink modal (#8160)

UX: Better composer hyperlink modal (#8160)

diff --git a/app/assets/javascripts/discourse/components/d-editor.js.es6 b/app/assets/javascripts/discourse/components/d-editor.js.es6
index 8cd3c54..dc2e9fe 100644
--- a/app/assets/javascripts/discourse/components/d-editor.js.es6
+++ b/app/assets/javascripts/discourse/components/d-editor.js.es6
@@ -21,6 +21,7 @@ import { wantsNewWindow } from "discourse/lib/intercept-click";
 import { translations } from "pretty-text/emoji/data";
 import { emojiSearch, isSkinTonableEmoji } from "pretty-text/emoji";
 import { emojiUrlFor } from "discourse/lib/text";
+import showModal from "discourse/lib/show-modal";
 
 // Our head can be a static string or a function that returns a string
 // based on input (like for numbered lists).
@@ -89,7 +90,7 @@ class Toolbar {
         id: "link",
         group: "insertions",
         shortcut: "K",
-        action: (...args) => this.context.send("showLinkModal", args)
+        sendAction: event => this.context.send("showLinkModal", event)
       });
     }
 
@@ -213,9 +214,6 @@ export function onToolbarCreate(func) {
 export default Ember.Component.extend({
   classNames: ["d-editor"],
   ready: false,
-  insertLinkHidden: true,
-  linkUrl: "",
-  linkText: "",
   lastSel: null,
   _mouseTrap: null,
   showLink: true,
@@ -946,21 +944,23 @@ export default Ember.Component.extend({
       }
     },
 
-    showLinkModal() {
+    showLinkModal(toolbarEvent) {
       if (this.disabled) {
         return;
       }
 
-      this.set("linkUrl", "");
-      this.set("linkText", "");
-
+      let linkText = "";
       this._lastSel = this._getSelected();
 
       if (this._lastSel) {
-        this.set("linkText", this._lastSel.value.trim());
+        linkText = this._lastSel.value.trim();
       }
 
-      this.set("insertLinkHidden", false);
+      showModal("insert-hyperlink").setProperties({
+        linkText: linkText,
+        _lastSel: this._lastSel,
+        toolbarEvent
+      });
     },
 
     formatCode() {
@@ -1004,29 +1004,6 @@ export default Ember.Component.extend({
           );
         }
       }
-    },
-
-    insertLink() {
-      const origLink = this.linkUrl;
-      const linkUrl =
-        origLink.indexOf("://") === -1 ? `http://${origLink}` : origLink;
-      const sel = this._lastSel;
-
-      if (Ember.isEmpty(linkUrl)) {
-        return;
-      }
-
-      const linkText = this.linkText || "";
-      if (linkText.length) {
-        this._addText(sel, `[${linkText}](${linkUrl})`);
-      } else {
-        if (sel.value) {
-          this._addText(sel, `[${sel.value}](${linkUrl})`);
-        } else {
-          this._addText(sel, `[${origLink}](${linkUrl})`);
-          this._selectText(sel.start + 1, origLink.length);
-        }
-      }
     }
   }
 });
diff --git a/app/assets/javascripts/discourse/controllers/insert-hyperlink.js.es6 b/app/assets/javascripts/discourse/controllers/insert-hyperlink.js.es6
new file mode 100644
index 0000000..d4f15d9
--- /dev/null
+++ b/app/assets/javascripts/discourse/controllers/insert-hyperlink.js.es6
@@ -0,0 +1,46 @@
+import ModalFunctionality from "discourse/mixins/modal-functionality";
+
+export default Ember.Controller.extend(ModalFunctionality, {
+  linkUrl: "",
+  linkText: "",
+
+  onShow() {
+    Ember.run.next(() =>
+      $(this)
+        .find("input.link-url")
+        .focus()
+    );
+  },
+
+  actions: {
+    ok() {
+      const origLink = this.linkUrl;
+      const linkUrl =
+        origLink.indexOf("://") === -1 ? `http://${origLink}` : origLink;
+      const sel = this._lastSel;
+
+      if (Ember.isEmpty(linkUrl)) {
+        return;
+      }
+
+      const linkText = this.linkText || "";
+
+      if (linkText.length) {
+        this.toolbarEvent.addText(`[${linkText}](${linkUrl})`);
+      } else {
+        if (sel.value) {
+          this.toolbarEvent.addText(`[${sel.value}](${linkUrl})`);
+        } else {
+          this.toolbarEvent.addText(`[${origLink}](${linkUrl})`);
+          this.toolbarEvent.selectText(sel.start + 1, origLink.length);
+        }
+      }
+      this.set("linkUrl", "");
+      this.set("linkText", "");
+      this.send("closeModal");
+    },
+    cancel() {
+      this.send("closeModal");
+    }
+  }
+});
diff --git a/app/assets/javascripts/discourse/templates/components/d-editor.hbs b/app/assets/javascripts/discourse/templates/components/d-editor.hbs
index 1f82566..9435d74 100644
--- a/app/assets/javascripts/discourse/templates/components/d-editor.hbs
+++ b/app/assets/javascripts/discourse/templates/components/d-editor.hbs
@@ -1,13 +1,3 @@
-<div class='d-editor-overlay hidden'></div>
-
-<div class='d-editor-modals'>
-  {{#d-editor-modal class="insert-link" hidden=insertLinkHidden okAction=(action "insertLink")}}
-    <h3>{{i18n "composer.link_dialog_title"}}</h3>
-    {{text-field value=linkUrl placeholderKey="composer.link_url_placeholder" class="link-url"}}
-    {{text-field value=linkText placeholderKey="composer.link_optional_text" class="link-text"}}
-  {{/d-editor-modal}}
-</div>
-
 <div class='d-editor-container'>
   <div class="d-editor-textarea-wrapper {{if disabled "disabled"}}">
     <div class='d-editor-button-bar'>
diff --git a/app/assets/javascripts/discourse/templates/modal/insert-hyperlink.hbs b/app/assets/javascripts/discourse/templates/modal/insert-hyperlink.hbs
new file mode 100644
index 0000000..8aa0607
--- /dev/null
+++ b/app/assets/javascripts/discourse/templates/modal/insert-hyperlink.hbs
@@ -0,0 +1,13 @@
+{{#d-modal-body title="composer.link_dialog_title" class="insert-link"}}
+    <div class="inputs">
+      {{text-field value=linkUrl placeholderKey="composer.link_url_placeholder" class="link-url"}}
+    </div>
+    <div class="inputs">
+      {{text-field value=linkText placeholderKey="composer.link_optional_text" class="link-text"}}
+    </div>
+{{/d-modal-body}}
+
+<div class="modal-footer">
+  {{d-button class="btn-primary" label="composer.modal_ok" action=(action "ok")}}
+  {{d-button class="btn-danger" label="composer.modal_cancel" action=(action "cancel")}}
+</div>
diff --git a/app/assets/stylesheets/common/base/modal.scss b/app/assets/stylesheets/common/base/modal.scss
index 5272245..bf181bf 100644
--- a/app/assets/stylesheets/common/base/modal.scss
+++ b/app/assets/stylesheets/common/base/modal.scss
@@ -199,6 +199,12 @@
     &.full-height-modal {
       max-height: calc(100vh - 150px);
     }
+
+    &.insert-link {
+      input {
+        min-width: 300px;
+      }
+    }
     textarea {
       width: 99%;
       height: 80px;
diff --git a/app/assets/stylesheets/common/d-editor.scss b/app/assets/stylesheets/common/d-editor.scss
index 4937bc7..89aa1e9 100644
--- a/app/assets/stylesheets/common/d-editor.scss
+++ b/app/assets/stylesheets/common/d-editor.scss
@@ -4,43 +4,12 @@
   max-width: 100%;
 }
 
-.d-editor-overlay {
-  position: absolute;
-  background-color: black;
-  opacity: 0.8;
-  z-index: z("modal", "overlay");
-}
-
-.d-editor-modals {
-  position: absolute;
-  z-index: z("modal", "content");
-}
-
 .d-editor {
   display: flex;
   flex-grow: 1;
   min-height: 0;
 }
 
-.d-editor .d-editor-modal {
-  min-width: 400px;
-  @media screen and (max-width: 424px) {
-    min-width: 300px;
-  }
-  position: absolute;
-  background-color: $secondary;
-  border: 1px solid $primary-low;
-  padding: 1em;
-  top: 25px;
-
-  input {
-    width: 95%;
-  }
-  h3 {
-    margin-bottom: 0.5em;
-  }
-}
-
 .d-editor-textarea-wrapper,
 .d-editor-preview-wrapper {
   flex: 1;
diff --git a/app/assets/stylesheets/desktop/modal.scss b/app/assets/stylesheets/desktop/modal.scss
index 861c624..9a2adb9 100644
--- a/app/assets/stylesheets/desktop/modal.scss
+++ b/app/assets/stylesheets/desktop/modal.scss
@@ -44,6 +44,12 @@
   .category-chooser {
     width: 50%;
   }
+
+  .modal-body.insert-link {
+    input {
+      min-width: 450px;
+    }
+  }
 }
 
 .edit-category-modal {
diff --git a/test/javascripts/acceptance/composer-hyperlink-test.js.es6 b/test/javascripts/acceptance/composer-hyperlink-test.js.es6
new file mode 100644
index 0000000..52ff6c7

[... diff too long, it was truncated ...]

GitHub sha: 30cda176