Convert `Ui/Page/Search/Input` to TypeScript
authorAlexander Ebert <ebert@woltlab.com>
Wed, 28 Oct 2020 18:58:27 +0000 (19:58 +0100)
committerAlexander Ebert <ebert@woltlab.com>
Wed, 28 Oct 2020 18:58:27 +0000 (19:58 +0100)
wcfsetup/install/files/js/WoltLabSuite/Core/Ui/Page/Search/Handler.js
wcfsetup/install/files/js/WoltLabSuite/Core/Ui/Page/Search/Input.js
wcfsetup/install/files/js/WoltLabSuite/Core/Ui/Search/Data.js [new file with mode: 0644]
wcfsetup/install/files/ts/WoltLabSuite/Core/Ui/Page/Search/Handler.js [deleted file]
wcfsetup/install/files/ts/WoltLabSuite/Core/Ui/Page/Search/Handler.ts [new file with mode: 0644]
wcfsetup/install/files/ts/WoltLabSuite/Core/Ui/Page/Search/Input.js [deleted file]
wcfsetup/install/files/ts/WoltLabSuite/Core/Ui/Page/Search/Input.ts [new file with mode: 0644]
wcfsetup/install/files/ts/WoltLabSuite/Core/Ui/Search/Data.ts [new file with mode: 0644]
wcfsetup/install/files/ts/WoltLabSuite/Core/Ui/Search/Input.ts

index 660a0ab2c5d5ddc8c25f5bb149a31e568ef28f61..6516b3a02db5db70088533aca647f344a3faa4f6 100644 (file)
  * Provides access to the lookup function of page handlers, allowing the user to search and
  * select page object ids.
  *
- * @author     Alexander Ebert
- * @copyright  2001-2019 WoltLab GmbH
- * @license    GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
- * @module     WoltLabSuite/Core/Ui/Page/Search/Handler
+ * @author  Alexander Ebert
+ * @copyright  2001-2019 WoltLab GmbH
+ * @license  GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module  WoltLabSuite/Core/Ui/Page/Search/Handler
  */
-define(['Language', 'StringUtil', 'Dom/Util', 'Ui/Dialog', './Input'], function (Language, StringUtil, DomUtil, UiDialog, UiPageSearchInput) {
+define(["require", "exports", "tslib", "../../../Language", "../../../StringUtil", "../../../Dom/Util", "../../Dialog", "./Input"], function (require, exports, tslib_1, Language, StringUtil, Util_1, Dialog_1, Input_1) {
     "use strict";
-    var _callback = null;
-    var _searchInput = null;
-    var _searchInputLabel = null;
-    var _searchInputHandler = null;
-    var _resultList = null;
-    var _resultListContainer = null;
-    /**
-     * @exports     WoltLabSuite/Core/Ui/Page/Search/Handler
-     */
-    return {
+    Object.defineProperty(exports, "__esModule", { value: true });
+    exports.open = void 0;
+    Language = tslib_1.__importStar(Language);
+    StringUtil = tslib_1.__importStar(StringUtil);
+    Util_1 = tslib_1.__importDefault(Util_1);
+    Dialog_1 = tslib_1.__importDefault(Dialog_1);
+    Input_1 = tslib_1.__importDefault(Input_1);
+    class UiPageSearchHandler {
+        constructor() {
+            this.callbackSuccess = undefined;
+            this.resultList = undefined;
+            this.resultListContainer = undefined;
+            this.searchInput = undefined;
+            this.searchInputHandler = undefined;
+            this.searchInputLabel = undefined;
+        }
         /**
          * Opens the lookup overlay for provided page id.
-         *
-         * @param      {int}           pageId                  page id
-         * @param      {string}        title                   dialog title
-         * @param      {function}      callback                callback function provided with the user-selected object id
-         * @param      {string?}       labelLanguageItem       optional language item name for the search input label
          */
-        open: function (pageId, title, callback, labelLanguageItem) {
-            _callback = callback;
-            UiDialog.open(this);
-            UiDialog.setTitle(this, title);
-            if (labelLanguageItem) {
-                _searchInputLabel.textContent = Language.get(labelLanguageItem);
-            }
-            else {
-                _searchInputLabel.textContent = Language.get('wcf.page.pageObjectID.search.terms');
-            }
+        open(pageId, title, callback, labelLanguageItem) {
+            this.callbackSuccess = callback;
+            Dialog_1.default.open(this);
+            Dialog_1.default.setTitle(this, title);
+            this.searchInputLabel.textContent = Language.get(labelLanguageItem || 'wcf.page.pageObjectID.search.terms');
             this._getSearchInputHandler().setPageId(pageId);
-        },
+        }
         /**
          * Builds the result list.
-         *
-         * @param       {Object}        data            AJAX response data
-         * @protected
          */
-        _buildList: function (data) {
-            this._resetList();
-            // no matches
+        buildList(data) {
+            this.resetList();
             if (!Array.isArray(data.returnValues) || data.returnValues.length === 0) {
-                elInnerError(_searchInput, Language.get('wcf.page.pageObjectID.search.noResults'));
+                Util_1.default.innerError(this.searchInput, Language.get('wcf.page.pageObjectID.search.noResults'));
                 return;
             }
-            var image, item, listItem;
-            for (var i = 0, length = data.returnValues.length; i < length; i++) {
-                item = data.returnValues[i];
-                image = item.image;
+            data.returnValues.forEach(item => {
+                let image = item.image;
                 if (/^fa-/.test(image)) {
-                    image = '<span class="icon icon48 ' + image + ' pointer jsTooltip" title="' + Language.get('wcf.global.select') + '"></span>';
+                    image = `<span class="icon icon48 ${image} pointer jsTooltip" title="${Language.get('wcf.global.select')}"></span>`;
                 }
-                listItem = elCreate('li');
-                elData(listItem, 'object-id', item.objectID);
-                listItem.innerHTML = '<div class="box48">'
-                    + image
-                    + '<div>'
-                    + '<div class="containerHeadline">'
-                    + '<h3><a href="' + StringUtil.escapeHTML(item.link) + '">' + StringUtil.escapeHTML(item.title) + '</a></h3>'
-                    + (item.description ? '<p>' + item.description + '</p>' : '')
-                    + '</div>'
-                    + '</div>'
-                    + '</div>';
-                listItem.addEventListener(WCF_CLICK_EVENT, this._click.bind(this));
-                _resultList.appendChild(listItem);
-            }
-            elShow(_resultListContainer);
-        },
+                const listItem = document.createElement('li');
+                listItem.dataset.objectId = item.objectID.toString();
+                const description = item.description ? `<p>${item.description}</p>` : '';
+                listItem.innerHTML = `<div class="box48">
+        ${image}
+        <div>
+          <div class="containerHeadline">
+            <h3>
+                <a href="${StringUtil.escapeHTML(item.link)}">${StringUtil.escapeHTML(item.title)}</a>
+            </h3>
+          ${description}
+          </div>
+        </div>
+      </div>`;
+                listItem.addEventListener('click', this.click.bind(this));
+                this.resultList.appendChild(listItem);
+            });
+            Util_1.default.show(this.resultListContainer);
+        }
         /**
          * Resets the list and removes any error elements.
-         *
-         * @protected
          */
-        _resetList: function () {
-            elInnerError(_searchInput, false);
-            _resultList.innerHTML = '';
-            elHide(_resultListContainer);
-        },
+        resetList() {
+            Util_1.default.innerError(this.searchInput, false);
+            this.resultList.innerHTML = '';
+            Util_1.default.hide(this.resultListContainer);
+        }
         /**
          * Initializes the search input handler and returns the instance.
-         *
-         * @returns     {UiPageSearchInput}     search input handler
-         * @protected
          */
-        _getSearchInputHandler: function () {
-            if (_searchInputHandler === null) {
-                var callback = this._buildList.bind(this);
-                _searchInputHandler = new UiPageSearchInput(elById('wcfUiPageSearchInput'), {
-                    callbackSuccess: callback
+        _getSearchInputHandler() {
+            if (!this.searchInputHandler) {
+                const input = document.getElementById('wcfUiPageSearchInput');
+                this.searchInputHandler = new Input_1.default(input, {
+                    callbackSuccess: this.buildList.bind(this),
                 });
             }
-            return _searchInputHandler;
-        },
+            return this.searchInputHandler;
+        }
         /**
          * Handles clicks on the item unless the click occurred directly on a link.
-         *
-         * @param       {Event}         event           event object
-         * @protected
          */
-        _click: function (event) {
-            if (event.target.nodeName === 'A') {
+        click(event) {
+            const clickTarget = event.target;
+            if (clickTarget.nodeName === 'A') {
                 return;
             }
             event.stopPropagation();
-            _callback(elData(event.currentTarget, 'object-id'));
-            UiDialog.close(this);
-        },
-        _dialogSetup: function () {
+            const eventTarget = event.currentTarget;
+            this.callbackSuccess(+eventTarget.dataset.objectId);
+            Dialog_1.default.close(this);
+        }
+        _dialogSetup() {
             return {
                 id: 'wcfUiPageSearchHandler',
                 options: {
-                    onShow: function () {
-                        if (_searchInput === null) {
-                            _searchInput = elById('wcfUiPageSearchInput');
-                            _searchInputLabel = _searchInput.parentNode.previousSibling.childNodes[0];
-                            _resultList = elById('wcfUiPageSearchResultList');
-                            _resultListContainer = elById('wcfUiPageSearchResultListContainer');
+                    onShow: (content) => {
+                        if (!this.searchInput) {
+                            this.searchInput = document.getElementById('wcfUiPageSearchInput');
+                            this.searchInputLabel = content.querySelector('label[for="wcfUiPageSearchInput"]');
+                            this.resultList = document.getElementById('wcfUiPageSearchResultList');
+                            this.resultListContainer = document.getElementById('wcfUiPageSearchResultListContainer');
                         }
                         // clear search input
-                        _searchInput.value = '';
+                        this.searchInput.value = '';
                         // reset results
-                        elHide(_resultListContainer);
-                        _resultList.innerHTML = '';
-                        _searchInput.focus();
+                        Util_1.default.hide(this.resultListContainer);
+                        this.resultList.innerHTML = '';
+                        this.searchInput.focus();
                     },
-                    title: ''
+                    title: '',
                 },
-                source: '<div class="section">'
-                    + '<dl>'
-                    + '<dt><label for="wcfUiPageSearchInput">' + Language.get('wcf.page.pageObjectID.search.terms') + '</label></dt>'
-                    + '<dd>'
-                    + '<input type="text" id="wcfUiPageSearchInput" class="long">'
-                    + '</dd>'
-                    + '</dl>'
-                    + '</div>'
-                    + '<section id="wcfUiPageSearchResultListContainer" class="section sectionContainerList">'
-                    + '<header class="sectionHeader">'
-                    + '<h2 class="sectionTitle">' + Language.get('wcf.page.pageObjectID.search.results') + '</h2>'
-                    + '</header>'
-                    + '<ul id="wcfUiPageSearchResultList" class="containerList wcfUiPageSearchResultList"></ul>'
-                    + '</section>'
+                source: `<div class="section">
+        <dl>
+          <dt>
+            <label for="wcfUiPageSearchInput">${Language.get('wcf.page.pageObjectID.search.terms')}</label>
+          </dt>
+          <dd>
+            <input type="text" id="wcfUiPageSearchInput" class="long">
+          </dd>
+        </dl>
+      </div>
+      <section id="wcfUiPageSearchResultListContainer" class="section sectionContainerList">
+        <header class="sectionHeader">
+          <h2 class="sectionTitle">${Language.get('wcf.page.pageObjectID.search.results')}</h2>
+        </header>
+        <ul id="wcfUiPageSearchResultList" class="containerList wcfUiPageSearchResultList"></ul>
+      </section>`,
             };
         }
-    };
+    }
+    let uiPageSearchHandler = undefined;
+    function getUiPageSearchHandler() {
+        if (!uiPageSearchHandler) {
+            uiPageSearchHandler = new UiPageSearchHandler();
+        }
+        return uiPageSearchHandler;
+    }
+    /**
+     * Opens the lookup overlay for provided page id.
+     *
+     * @param  {int}    pageId      page id
+     * @param  {string}  title      dialog title
+     * @param  {function}  callback    callback function provided with the user-selected object id
+     * @param  {string?}  labelLanguageItem  optional language item name for the search input label
+     */
+    function open(pageId, title, callback, labelLanguageItem) {
+        getUiPageSearchHandler().open(pageId, title, callback, labelLanguageItem);
+    }
+    exports.open = open;
 });
index 6afb29c9b8b2a77daab0b8333a2afe50f8088f40..306df39044557ec95de54b566a63ced081917a0b 100644 (file)
@@ -1,50 +1,43 @@
 /**
  * Suggestions for page object ids with external response data processing.
  *
- * @author     Alexander Ebert
- * @copyright  2001-2019 WoltLab GmbH
- * @license    GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
- * @module     WoltLabSuite/Core/Ui/Page/Search/Input
- * @extends     module:WoltLabSuite/Core/Ui/Search/Input
+ * @author  Alexander Ebert
+ * @copyright  2001-2019 WoltLab GmbH
+ * @license  GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module  WoltLabSuite/Core/Ui/Page/Search/Input
  */
-define(['Core', 'WoltLabSuite/Core/Ui/Search/Input'], function (Core, UiSearchInput) {
+define(["require", "exports", "tslib", "../../../Core", "../../Search/Input"], function (require, exports, tslib_1, Core, Input_1) {
     "use strict";
-    /**
-     * @param       {Element}       element         input element
-     * @param       {Object=}       options         search options and settings
-     * @constructor
-     */
-    function UiPageSearchInput(element, options) { this.init(element, options); }
-    Core.inherit(UiPageSearchInput, UiSearchInput, {
-        init: function (element, options) {
+    Core = tslib_1.__importStar(Core);
+    Input_1 = tslib_1.__importDefault(Input_1);
+    class UiPageSearchInput extends Input_1.default {
+        constructor(element, options) {
+            if (typeof options.callbackSuccess !== 'function') {
+                throw new Error("Expected a valid callback function for 'callbackSuccess'.");
+            }
             options = Core.extend({
                 ajax: {
-                    className: 'wcf\\data\\page\\PageAction'
+                    className: 'wcf\\data\\page\\PageAction',
                 },
-                callbackSuccess: null
             }, options);
-            if (typeof options.callbackSuccess !== 'function') {
-                throw new Error("Expected a valid callback function for 'callbackSuccess'.");
-            }
-            UiPageSearchInput._super.prototype.init.call(this, element, options);
-            this._pageId = 0;
-        },
+            super(element, options);
+            this.callbackSuccess = options.callbackSuccess;
+            this.pageId = 0;
+        }
         /**
          * Sets the target page id.
-         *
-         * @param       {int}   pageId  target page id
          */
-        setPageId: function (pageId) {
-            this._pageId = pageId;
-        },
-        _getParameters: function (value) {
-            var data = UiPageSearchInput._super.prototype._getParameters.call(this, value);
-            data.objectIDs = [this._pageId];
+        setPageId(pageId) {
+            this.pageId = pageId;
+        }
+        getParameters(value) {
+            const data = super.getParameters(value);
+            data.objectIDs = [this.pageId];
             return data;
-        },
-        _ajaxSuccess: function (data) {
-            this._options.callbackSuccess(data);
         }
-    });
+        _ajaxSuccess(data) {
+            this.callbackSuccess(data);
+        }
+    }
     return UiPageSearchInput;
 });
diff --git a/wcfsetup/install/files/js/WoltLabSuite/Core/Ui/Search/Data.js b/wcfsetup/install/files/js/WoltLabSuite/Core/Ui/Search/Data.js
new file mode 100644 (file)
index 0000000..2ae92b6
--- /dev/null
@@ -0,0 +1,4 @@
+define(["require", "exports"], function (require, exports) {
+    "use strict";
+    Object.defineProperty(exports, "__esModule", { value: true });
+});
diff --git a/wcfsetup/install/files/ts/WoltLabSuite/Core/Ui/Page/Search/Handler.js b/wcfsetup/install/files/ts/WoltLabSuite/Core/Ui/Page/Search/Handler.js
deleted file mode 100644 (file)
index 00f8c6e..0000000
+++ /dev/null
@@ -1,180 +0,0 @@
-/**
- * Provides access to the lookup function of page handlers, allowing the user to search and
- * select page object ids.
- * 
- * @author     Alexander Ebert
- * @copyright  2001-2019 WoltLab GmbH
- * @license    GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
- * @module     WoltLabSuite/Core/Ui/Page/Search/Handler
- */
-define(['Language', 'StringUtil', 'Dom/Util', 'Ui/Dialog', './Input'], function(Language, StringUtil, DomUtil, UiDialog, UiPageSearchInput) {
-       "use strict";
-       
-       var _callback = null;
-       var _searchInput = null;
-       var _searchInputLabel = null;
-       var _searchInputHandler = null;
-       var _resultList = null;
-       var _resultListContainer = null;
-       
-       /**
-        * @exports     WoltLabSuite/Core/Ui/Page/Search/Handler
-        */
-       return {
-               /**
-                * Opens the lookup overlay for provided page id.
-                * 
-                * @param       {int}           pageId                  page id
-                * @param       {string}        title                   dialog title
-                * @param       {function}      callback                callback function provided with the user-selected object id
-                * @param       {string?}       labelLanguageItem       optional language item name for the search input label
-                */
-               open: function (pageId, title, callback, labelLanguageItem) {
-                       _callback = callback;
-                       
-                       UiDialog.open(this);
-                       UiDialog.setTitle(this, title);
-                       
-                       if (labelLanguageItem) {
-                               _searchInputLabel.textContent = Language.get(labelLanguageItem);
-                       }
-                       else {
-                               _searchInputLabel.textContent = Language.get('wcf.page.pageObjectID.search.terms');
-                       }
-                       
-                       this._getSearchInputHandler().setPageId(pageId);
-               },
-               
-               /**
-                * Builds the result list.
-                * 
-                * @param       {Object}        data            AJAX response data
-                * @protected
-                */
-               _buildList: function(data) {
-                       this._resetList();
-                       
-                       // no matches
-                       if (!Array.isArray(data.returnValues) || data.returnValues.length === 0) {
-                               elInnerError(_searchInput, Language.get('wcf.page.pageObjectID.search.noResults'));
-                               
-                               return;
-                       }
-                       
-                       var image, item, listItem;
-                       for (var i = 0, length = data.returnValues.length; i < length; i++) {
-                               item = data.returnValues[i];
-                               image = item.image;
-                               if (/^fa-/.test(image)) {
-                                       image = '<span class="icon icon48 ' + image + ' pointer jsTooltip" title="' + Language.get('wcf.global.select') + '"></span>';
-                               }
-                               
-                               listItem = elCreate('li');
-                               elData(listItem, 'object-id', item.objectID);
-                               
-                               listItem.innerHTML = '<div class="box48">'
-                                       + image
-                                       + '<div>'
-                                               + '<div class="containerHeadline">'
-                                                       + '<h3><a href="' + StringUtil.escapeHTML(item.link) + '">' + StringUtil.escapeHTML(item.title) + '</a></h3>'
-                                                       + (item.description ? '<p>' + item.description + '</p>' : '')
-                                               + '</div>'
-                                       + '</div>'
-                               + '</div>';
-                               
-                               listItem.addEventListener(WCF_CLICK_EVENT, this._click.bind(this));
-                               
-                               _resultList.appendChild(listItem);
-                       }
-                       
-                       elShow(_resultListContainer);
-               },
-               
-               /**
-                * Resets the list and removes any error elements.
-                * 
-                * @protected
-                */
-               _resetList: function() {
-                       elInnerError(_searchInput, false);
-                       
-                       _resultList.innerHTML = '';
-                       
-                       elHide(_resultListContainer);
-               },
-               
-               /**
-                * Initializes the search input handler and returns the instance.
-                * 
-                * @returns     {UiPageSearchInput}     search input handler
-                * @protected
-                */
-               _getSearchInputHandler: function() {
-                       if (_searchInputHandler === null) {
-                               var callback = this._buildList.bind(this);
-                               _searchInputHandler = new UiPageSearchInput(elById('wcfUiPageSearchInput'), {
-                                       callbackSuccess: callback
-                               });
-                       }
-                       
-                       return _searchInputHandler;
-               },
-               
-               /**
-                * Handles clicks on the item unless the click occurred directly on a link.
-                * 
-                * @param       {Event}         event           event object
-                * @protected
-                */
-               _click: function(event) {
-                       if (event.target.nodeName === 'A') {
-                               return;
-                       }
-                       
-                       event.stopPropagation();
-                       
-                       _callback(elData(event.currentTarget, 'object-id'));
-                       UiDialog.close(this);
-               },
-               
-               _dialogSetup: function() {
-                       return {
-                               id: 'wcfUiPageSearchHandler',
-                               options: {
-                                       onShow: function() {
-                                               if (_searchInput === null) {
-                                                       _searchInput = elById('wcfUiPageSearchInput');
-                                                       _searchInputLabel = _searchInput.parentNode.previousSibling.childNodes[0];
-                                                       _resultList = elById('wcfUiPageSearchResultList');
-                                                       _resultListContainer = elById('wcfUiPageSearchResultListContainer');
-                                               }
-                                               
-                                               // clear search input
-                                               _searchInput.value = '';
-                                               
-                                               // reset results
-                                               elHide(_resultListContainer);
-                                               _resultList.innerHTML = '';
-                                               
-                                               _searchInput.focus();
-                                       },
-                                       title: ''
-                               },
-                               source: '<div class="section">'
-                                               + '<dl>'
-                                                       + '<dt><label for="wcfUiPageSearchInput">' + Language.get('wcf.page.pageObjectID.search.terms') + '</label></dt>'
-                                                       + '<dd>'
-                                                               + '<input type="text" id="wcfUiPageSearchInput" class="long">'
-                                                       + '</dd>'
-                                               + '</dl>'
-                                       + '</div>'
-                                       + '<section id="wcfUiPageSearchResultListContainer" class="section sectionContainerList">'
-                                               + '<header class="sectionHeader">'
-                                                       + '<h2 class="sectionTitle">' + Language.get('wcf.page.pageObjectID.search.results') + '</h2>'
-                                               + '</header>'
-                                               + '<ul id="wcfUiPageSearchResultList" class="containerList wcfUiPageSearchResultList"></ul>'
-                                       + '</section>'
-                       };
-               }
-       };
-});
diff --git a/wcfsetup/install/files/ts/WoltLabSuite/Core/Ui/Page/Search/Handler.ts b/wcfsetup/install/files/ts/WoltLabSuite/Core/Ui/Page/Search/Handler.ts
new file mode 100644 (file)
index 0000000..fdfd144
--- /dev/null
@@ -0,0 +1,200 @@
+/**
+ * Provides access to the lookup function of page handlers, allowing the user to search and
+ * select page object ids.
+ *
+ * @author  Alexander Ebert
+ * @copyright  2001-2019 WoltLab GmbH
+ * @license  GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module  WoltLabSuite/Core/Ui/Page/Search/Handler
+ */
+
+import * as Language from '../../../Language';
+import * as StringUtil from '../../../StringUtil';
+import DomUtil from '../../../Dom/Util';
+import UiDialog from '../../Dialog';
+import UiPageSearchInput from './Input';
+import { DatabaseObjectActionResponse } from '../../../Ajax/Data';
+
+type CallbackSelect = (objectId: number) => void
+
+interface ItemData {
+  description?: string;
+  image: string;
+  link: string;
+  objectID: number;
+  title: string;
+}
+
+interface AjaxResponse extends DatabaseObjectActionResponse {
+  returnValues: ItemData[];
+}
+
+class UiPageSearchHandler {
+  private callbackSuccess?: CallbackSelect = undefined;
+  private resultList?: HTMLUListElement = undefined;
+  private resultListContainer?: HTMLElement = undefined;
+  private searchInput?: HTMLInputElement = undefined;
+  private searchInputHandler?: UiPageSearchInput = undefined;
+  private searchInputLabel?: HTMLLabelElement = undefined;
+
+  /**
+   * Opens the lookup overlay for provided page id.
+   */
+  open(pageId: number, title: string, callback: CallbackSelect, labelLanguageItem?: string): void {
+    this.callbackSuccess = callback;
+
+    UiDialog.open(this);
+    UiDialog.setTitle(this, title);
+
+    this.searchInputLabel!.textContent = Language.get(labelLanguageItem || 'wcf.page.pageObjectID.search.terms');
+
+    this._getSearchInputHandler().setPageId(pageId);
+  }
+
+  /**
+   * Builds the result list.
+   */
+  private buildList(data: AjaxResponse): void {
+    this.resetList();
+
+    if (!Array.isArray(data.returnValues) || data.returnValues.length === 0) {
+      DomUtil.innerError(this.searchInput!, Language.get('wcf.page.pageObjectID.search.noResults'));
+      return;
+    }
+
+    data.returnValues.forEach(item => {
+      let image = item.image;
+      if (/^fa-/.test(image)) {
+        image = `<span class="icon icon48 ${image} pointer jsTooltip" title="${Language.get('wcf.global.select')}"></span>`;
+      }
+
+      const listItem = document.createElement('li');
+      listItem.dataset.objectId = item.objectID.toString();
+
+      const description = item.description ? `<p>${item.description}</p>` : '' 
+      listItem.innerHTML = `<div class="box48">
+        ${image}
+        <div>
+          <div class="containerHeadline">
+            <h3>
+                <a href="${StringUtil.escapeHTML(item.link)}">${StringUtil.escapeHTML(item.title)}</a>
+            </h3>
+          ${description}
+          </div>
+        </div>
+      </div>`;
+
+      listItem.addEventListener('click', this.click.bind(this));
+
+      this.resultList!.appendChild(listItem);
+    });
+
+    DomUtil.show(this.resultListContainer!);
+  }
+
+  /**
+   * Resets the list and removes any error elements.
+   */
+  private resetList(): void {
+    DomUtil.innerError(this.searchInput!, false);
+
+    this.resultList!.innerHTML = '';
+
+    DomUtil.hide(this.resultListContainer!);
+  }
+
+  /**
+   * Initializes the search input handler and returns the instance.
+   */
+  _getSearchInputHandler(): UiPageSearchInput {
+    if (!this.searchInputHandler) {
+      const input = document.getElementById('wcfUiPageSearchInput') as HTMLInputElement;
+      this.searchInputHandler = new UiPageSearchInput(input, {
+        callbackSuccess: this.buildList.bind(this),
+      });
+    }
+
+    return this.searchInputHandler;
+  }
+
+  /**
+   * Handles clicks on the item unless the click occurred directly on a link.
+   */
+  private click(event: MouseEvent): void {
+    const clickTarget = event.target as HTMLElement;
+    if (clickTarget.nodeName === 'A') {
+      return;
+    }
+
+    event.stopPropagation();
+
+    const eventTarget = event.currentTarget as HTMLElement;
+    this.callbackSuccess!(+eventTarget.dataset.objectId!);
+
+    UiDialog.close(this);
+  }
+
+  _dialogSetup() {
+    return {
+      id: 'wcfUiPageSearchHandler',
+      options: {
+        onShow: (content: HTMLElement): void => {
+          if (!this.searchInput) {
+            this.searchInput = document.getElementById('wcfUiPageSearchInput') as HTMLInputElement;
+            this.searchInputLabel = content.querySelector('label[for="wcfUiPageSearchInput"]') as HTMLLabelElement;
+            this.resultList = document.getElementById('wcfUiPageSearchResultList') as HTMLUListElement;
+            this.resultListContainer = document.getElementById('wcfUiPageSearchResultListContainer') as HTMLElement;
+          }
+
+          // clear search input
+          this.searchInput.value = '';
+
+          // reset results
+          DomUtil.hide(this.resultListContainer!);
+          this.resultList!.innerHTML = '';
+
+          this.searchInput.focus();
+        },
+        title: '',
+      },
+      source: `<div class="section">
+        <dl>
+          <dt>
+            <label for="wcfUiPageSearchInput">${Language.get('wcf.page.pageObjectID.search.terms')}</label>
+          </dt>
+          <dd>
+            <input type="text" id="wcfUiPageSearchInput" class="long">
+          </dd>
+        </dl>
+      </div>
+      <section id="wcfUiPageSearchResultListContainer" class="section sectionContainerList">
+        <header class="sectionHeader">
+          <h2 class="sectionTitle">${Language.get('wcf.page.pageObjectID.search.results')}</h2>
+        </header>
+        <ul id="wcfUiPageSearchResultList" class="containerList wcfUiPageSearchResultList"></ul>
+      </section>`,
+    };
+  }
+}
+
+let uiPageSearchHandler: UiPageSearchHandler | undefined = undefined;
+
+function getUiPageSearchHandler(): UiPageSearchHandler {
+  if (!uiPageSearchHandler) {
+    uiPageSearchHandler = new UiPageSearchHandler();
+  }
+
+  return uiPageSearchHandler;
+}
+
+/**
+ * Opens the lookup overlay for provided page id.
+ *
+ * @param  {int}    pageId      page id
+ * @param  {string}  title      dialog title
+ * @param  {function}  callback    callback function provided with the user-selected object id
+ * @param  {string?}  labelLanguageItem  optional language item name for the search input label
+ */
+export function open(pageId, title, callback, labelLanguageItem) {
+  getUiPageSearchHandler().open(pageId, title, callback, labelLanguageItem);
+}
diff --git a/wcfsetup/install/files/ts/WoltLabSuite/Core/Ui/Page/Search/Input.js b/wcfsetup/install/files/ts/WoltLabSuite/Core/Ui/Page/Search/Input.js
deleted file mode 100644 (file)
index e528308..0000000
+++ /dev/null
@@ -1,60 +0,0 @@
-/**
- * Suggestions for page object ids with external response data processing.
- * 
- * @author     Alexander Ebert
- * @copyright  2001-2019 WoltLab GmbH
- * @license    GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
- * @module     WoltLabSuite/Core/Ui/Page/Search/Input
- * @extends     module:WoltLabSuite/Core/Ui/Search/Input
- */
-define(['Core', 'WoltLabSuite/Core/Ui/Search/Input'], function(Core, UiSearchInput) {
-       "use strict";
-       
-       /**
-        * @param       {Element}       element         input element
-        * @param       {Object=}       options         search options and settings
-        * @constructor
-        */
-       function UiPageSearchInput(element, options) { this.init(element, options); }
-       Core.inherit(UiPageSearchInput, UiSearchInput, {
-               init: function(element, options) {
-                       options = Core.extend({
-                               ajax: {
-                                       className: 'wcf\\data\\page\\PageAction'
-                               },
-                               callbackSuccess: null
-                       }, options);
-                       
-                       if (typeof options.callbackSuccess !== 'function') {
-                               throw new Error("Expected a valid callback function for 'callbackSuccess'.");
-                       }
-                       
-                       UiPageSearchInput._super.prototype.init.call(this, element, options);
-                       
-                       this._pageId = 0;
-               },
-               
-               /**
-                * Sets the target page id.
-                * 
-                * @param       {int}   pageId  target page id
-                */
-               setPageId: function(pageId) {
-                       this._pageId = pageId;
-               },
-               
-               _getParameters: function(value) {
-                       var data = UiPageSearchInput._super.prototype._getParameters.call(this, value);
-                       
-                       data.objectIDs = [this._pageId];
-                       
-                       return data;
-               },
-               
-               _ajaxSuccess: function(data) {
-                       this._options.callbackSuccess(data);
-               }
-       });
-       
-       return UiPageSearchInput;
-});
diff --git a/wcfsetup/install/files/ts/WoltLabSuite/Core/Ui/Page/Search/Input.ts b/wcfsetup/install/files/ts/WoltLabSuite/Core/Ui/Page/Search/Input.ts
new file mode 100644 (file)
index 0000000..194fcd2
--- /dev/null
@@ -0,0 +1,63 @@
+/**
+ * Suggestions for page object ids with external response data processing.
+ *
+ * @author  Alexander Ebert
+ * @copyright  2001-2019 WoltLab GmbH
+ * @license  GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module  WoltLabSuite/Core/Ui/Page/Search/Input
+ */
+
+import * as Core from '../../../Core';
+import UiSearchInput from '../../Search/Input';
+import { SearchInputOptions } from '../../Search/Data';
+import { DatabaseObjectActionPayload, DatabaseObjectActionResponse } from '../../../Ajax/Data';
+
+type CallbackSuccess = (data: DatabaseObjectActionResponse) => void
+
+interface PageSearchOptions extends SearchInputOptions {
+  callbackSuccess: CallbackSuccess
+}
+
+class UiPageSearchInput extends UiSearchInput {
+  private readonly callbackSuccess: CallbackSuccess;
+  private pageId: number;
+
+  constructor(element: HTMLInputElement, options: PageSearchOptions) {
+    if (typeof options.callbackSuccess !== 'function') {
+      throw new Error("Expected a valid callback function for 'callbackSuccess'.");
+    }
+
+    options = Core.extend({
+      ajax: {
+        className: 'wcf\\data\\page\\PageAction',
+      },
+    }, options) as any;
+
+    super(element, options);
+
+    this.callbackSuccess = options.callbackSuccess;
+
+    this.pageId = 0;
+  }
+
+  /**
+   * Sets the target page id.
+   */
+  setPageId(pageId: number): void {
+    this.pageId = pageId;
+  }
+
+  protected getParameters(value: string): Partial<DatabaseObjectActionPayload> {
+    const data = super.getParameters(value);
+
+    data.objectIDs = [this.pageId];
+
+    return data;
+  }
+
+  _ajaxSuccess(data) {
+    this.callbackSuccess(data);
+  }
+}
+
+export = UiPageSearchInput;
diff --git a/wcfsetup/install/files/ts/WoltLabSuite/Core/Ui/Search/Data.ts b/wcfsetup/install/files/ts/WoltLabSuite/Core/Ui/Search/Data.ts
new file mode 100644 (file)
index 0000000..fba749a
--- /dev/null
@@ -0,0 +1,17 @@
+import { DatabaseObjectActionPayload } from '../../Ajax/Data';
+
+export type CallbackDropdownInit = (list: HTMLUListElement) => void
+
+export type CallbackSelect = (item: HTMLElement) => boolean
+
+export interface SearchInputOptions {
+  ajax?: Partial<DatabaseObjectActionPayload>;
+  autoFocus?: boolean;
+  callbackDropdownInit?: CallbackDropdownInit;
+  callbackSelect?: CallbackSelect;
+  delay?: number;
+  excludedSearchValues?: string[];
+  minLength?: number;
+  noResultPlaceholder?: string;
+  preventSubmit?: boolean;
+}
index 94cde1b080a3e5ea507d75e66dccff678d748eca..0f2fdce692d1a41590a06cb0710cc7d74732bb16 100644 (file)
@@ -13,6 +13,7 @@ import DomUtil from '../../Dom/Util';
 import UiDropdownSimple from '../Dropdown/Simple';
 import { DatabaseObjectActionPayload, DatabaseObjectActionResponse } from '../../Ajax/Data';
 import AjaxRequest from '../../Ajax/Request';
+import { CallbackDropdownInit, CallbackSelect, SearchInputOptions } from './Data';
 
 class UiSearchInput {
   private activeItem?: HTMLLIElement = undefined;
@@ -188,7 +189,7 @@ class UiSearchInput {
   /**
    * Returns additional AJAX parameters.
    */
-  private getParameters(value: string): Partial<DatabaseObjectActionPayload> {
+  protected getParameters(value: string): Partial<DatabaseObjectActionPayload> {
     return {
       parameters: {
         data: {
@@ -364,22 +365,6 @@ class UiSearchInput {
 
 export = UiSearchInput
 
-type CallbackDropdownInit = (list: HTMLUListElement) => void
-
-type CallbackSelect = (item: HTMLElement) => boolean
-
-interface SearchInputOptions {
-  ajax: Partial<DatabaseObjectActionPayload>;
-  autoFocus?: boolean;
-  callbackDropdownInit?: CallbackDropdownInit;
-  callbackSelect?: CallbackSelect;
-  delay?: number;
-  excludedSearchValues?: string[];
-  minLength?: number;
-  noResultPlaceholder?: string;
-  preventSubmit?: boolean;
-}
-
 interface ListItemData {
   label: string;
   objectID: number;