You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@myfaces.apache.org by lo...@apache.org on 2019/06/13 13:55:54 UTC

[myfaces-tobago] branch master updated: TOBAGO-1633: TS refactoring: focus

This is an automated email from the ASF dual-hosted git repository.

lofwyr pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/myfaces-tobago.git


The following commit(s) were added to refs/heads/master by this push:
     new 4342daa  TOBAGO-1633: TS refactoring: focus
4342daa is described below

commit 4342daa04d8042ac880403b3412e918558011e79
Author: Udo Schnurpfeil <lo...@apache.org>
AuthorDate: Thu Jun 13 15:55:46 2019 +0200

    TOBAGO-1633: TS refactoring: focus
---
 .../src/main/npm/package.json                      |   2 +-
 .../src/main/npm/ts/tobago-all.ts                  |   1 +
 .../src/main/npm/ts/tobago-core.ts                 |  70 -------------
 .../src/main/npm/ts/tobago-focus.ts                | 109 +++++++++++++++++++++
 .../src/main/npm/ts/tobago-in.ts                   |   3 +-
 .../src/main/resources/META-INF/tobago-config.xml  |   1 +
 6 files changed, 113 insertions(+), 73 deletions(-)

diff --git a/tobago-theme/tobago-theme-standard/src/main/npm/package.json b/tobago-theme/tobago-theme-standard/src/main/npm/package.json
index 8376704..2c8f1ba 100644
--- a/tobago-theme/tobago-theme-standard/src/main/npm/package.json
+++ b/tobago-theme/tobago-theme-standard/src/main/npm/package.json
@@ -24,7 +24,7 @@
     "css-prefix": "postcss --config postcss.config.js --replace \"css/*.css\" \"!css/*.min.css\"",
     "css-minify": "cleancss --level 1 --source-map --source-map-inline-sources --output css/tobago.min.css css/tobago.css",
     "ts-compile": "tsc",
-    "js-minify": "uglifyjs --compress typeofs=false,drop_console=true --mangle --source-map includeSources --output js/tobago.min.js js/tobago-myfaces.js js/tobago-deltaspike.js js/tobago-polyfill.js js/tobago-listener.js js/tobago-core.js js/tobago-dropdown.js js/tobago-calendar.js js/tobago-command.js js/tobago-file.js js/tobago-header-footer.js js/tobago-in.js js/tobago-jsf.js js/tobago-overlay.js js/tobago-panel.js js/tobago-popover.js js/tobago-popup.js js/tobago-reload.js js/tobago [...]
+    "js-minify": "uglifyjs --compress typeofs=false,drop_console=true --mangle --source-map includeSources --output js/tobago.min.js js/tobago-myfaces.js js/tobago-deltaspike.js js/tobago-polyfill.js js/tobago-listener.js js/tobago-core.js js/tobago-dropdown.js js/tobago-calendar.js js/tobago-command.js js/tobago-file.js js/tobago-focus.js js/tobago-header-footer.js js/tobago-in.js js/tobago-jsf.js js/tobago-overlay.js js/tobago-panel.js js/tobago-popover.js js/tobago-popup.js js/tobago- [...]
     "test": "jest"
   },
   "devDependencies": {
diff --git a/tobago-theme/tobago-theme-standard/src/main/npm/ts/tobago-all.ts b/tobago-theme/tobago-theme-standard/src/main/npm/ts/tobago-all.ts
index 9b231be..f8fda21 100644
--- a/tobago-theme/tobago-theme-standard/src/main/npm/ts/tobago-all.ts
+++ b/tobago-theme/tobago-theme-standard/src/main/npm/ts/tobago-all.ts
@@ -21,6 +21,7 @@ import "./tobago-dropdown";
 import "./tobago-calendar";
 import "./tobago-command";
 import "./tobago-file";
+import "./tobago-focus";
 import "./tobago-header-footer";
 import "./tobago-in";
 import "./tobago-jsf";
diff --git a/tobago-theme/tobago-theme-standard/src/main/npm/ts/tobago-core.ts b/tobago-theme/tobago-theme-standard/src/main/npm/ts/tobago-core.ts
index eabca9f..3e0428c 100644
--- a/tobago-theme/tobago-theme-standard/src/main/npm/ts/tobago-core.ts
+++ b/tobago-theme/tobago-theme-standard/src/main/npm/ts/tobago-core.ts
@@ -226,9 +226,6 @@ export class Tobago4 {
   static initDom = function (elements) {
     elements = elements.jQuery ? elements : jQuery(elements); // fixme jQuery -> ES5
 
-    // focus
-    Tobago4.initFocus(elements);
-
     Tobago4.initScrollPosition(elements ? elements : jQuery(".tobago-page"));
   };
 
@@ -259,73 +256,6 @@ export class Tobago4 {
     });
   };
 
-// -------- Util functions ----------------------------------------------------
-
-  /**
-   * Sets the focus to the requested element or to the first possible if
-   * no element is explicitly requested.
-   *
-   * The priority order is:
-   * - error (the first error element gets the focus)
-   * - auto (the element with the tobago tag attribute focus="true" gets the focus)
-   * - last (the element from the last request with same id gets the focus, not AJAX)
-   * - first (the first input element (without tabindex=-1) gets the focus, not AJAX)
-   */
-  static initFocus = function (elements) {
-
-    var $focusable = jQuery(":input:enabled:visible:not(button):not([tabindex='-1'])");
-    $focusable.focus(function () {
-      // remember the last focused element, for later
-      Tobago4.findSubElementOfPage("lastFocusId").val(jQuery(this).attr("id"));
-    });
-
-    var $hasDanger = Tobago4Utils.selectWithJQuery(elements, '.has-danger');
-    var $dangerInput = $hasDanger.find("*").filter(":input:enabled:visible:first");
-    if ($dangerInput.length > 0) {
-      Tobago4.setFocus($dangerInput);
-      return;
-    }
-
-    var $autoFocus = Tobago4Utils.selectWithJQuery(elements, '[autofocus]');
-    var hasAutoFocus = $autoFocus.length > 0;
-    if (hasAutoFocus) {
-      // nothing to do, because the browser make the work.
-
-      // autofocus in popups doesn't work automatically... so we fix that here
-      jQuery('.modal').on('shown.bs.modal', function () {
-        Tobago4.setFocus(jQuery(this).find('[autofocus]'));
-      });
-
-      return;
-    }
-
-    if (elements) {
-      // seems to be AJAX, so end here
-      return;
-    }
-
-    var lastFocusId = Tobago4.findSubElementOfPage("lastFocusId").get(0).getAttribute("value");
-    if (lastFocusId) {
-      Tobago4.setFocus(jQuery(DomUtils.escapeClientId(lastFocusId)));
-      return;
-    }
-
-    var $firstInput = jQuery(":input:enabled:visible:not(button):not([tabindex='-1']):first");
-    if ($firstInput.length > 0) {
-      Tobago4.setFocus($firstInput);
-      return;
-    }
-  };
-
-  static setFocus = function ($element) {
-    try {
-      // focus() on not visible elements breaks some IE
-      $element.focus();
-    } catch (e) {
-      console.error("element-id=" + $element.attr("id") + " exception=" + e);
-    }
-  };
-
   static toString = function (element) {
     var result = '';
     for (var property in element) {
diff --git a/tobago-theme/tobago-theme-standard/src/main/npm/ts/tobago-focus.ts b/tobago-theme/tobago-theme-standard/src/main/npm/ts/tobago-focus.ts
new file mode 100644
index 0000000..0fb3f87
--- /dev/null
+++ b/tobago-theme/tobago-theme-standard/src/main/npm/ts/tobago-focus.ts
@@ -0,0 +1,109 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import {Tobago4} from "./tobago-core";
+import {DomUtils} from "./tobago-utils";
+import {Listener, Phase} from "./tobago-listener";
+
+export class Focus {
+
+  private static getHidden(): HTMLInputElement {
+    return <HTMLInputElement>document.getElementById(DomUtils.page().id + Tobago4.SUB_COMPONENT_SEP + "lastFocusId");
+  }
+
+  static setLastFocusId(id: string): void {
+    this.getHidden().value = id;
+  }
+
+  static getLastFocusId(): string {
+    return this.getHidden().value;
+  }
+
+  /**
+   * Sets the focus to the requested element or to the first possible if
+   * no element is explicitly requested.
+   *
+   * The priority order is:
+   * - error (the first error element gets the focus)
+   * - auto (the element with the tobago tag attribute focus="true" gets the focus)
+   * - last (the element from the last request with same id gets the focus, not AJAX)
+   * - first (the first input element (without tabindex=-1) gets the focus, not AJAX)
+   */
+  static init = function (element: HTMLElement) {
+
+    const activeInputs = Focus.activeInputs(element);
+
+    for (const focusable of activeInputs) {
+      focusable.addEventListener("focus", function (event: FocusEvent) {
+        const target = <HTMLElement>event.target;
+        if (target.style.visibility !== "hidden" && target.style.display != "none") {
+          // remember the last focused element, for later
+          Focus.setLastFocusId(target.id);
+        }
+      });
+    }
+
+    for (const hasDanger of DomUtils.selfOrElementsByClassName(element, ".has-danger")) {
+      const activeInputsInsideDanger = Focus.activeInputs(hasDanger);
+      if (activeInputsInsideDanger.length > 0) {
+        activeInputsInsideDanger[0].focus();
+        return;
+      }
+    }
+
+    const autoFocus = element.querySelector("[autofocus]");
+    if (autoFocus) {
+      // nothing to do, because the browser make the work.
+
+      // autofocus in popups doesn't work automatically... so we fix that here
+      jQuery('.modal').on('shown.bs.modal', function () {
+        jQuery(this).find('[autofocus]').get(0).focus();
+      });
+
+      return;
+    }
+
+    if (element.parentElement) {
+      // seems to be AJAX, so end here
+      return;
+    }
+
+    const lastFocusId = Focus.getLastFocusId();
+    if (lastFocusId) {
+      document.getElementById(lastFocusId).focus();
+      return;
+    }
+
+    if (activeInputs.length > 0) {
+      activeInputs[0].focus();
+      return;
+    }
+  };
+
+  private static activeInputs(element: HTMLElement) {
+    return DomUtils.selfOrQuerySelectorAll(element,
+        "input:not([type='hidden']):not([disabled]):not([tabindex='-1'])," +
+        "select:not([disabled]):not([tabindex='-1'])," +
+        "textarea:not([disabled]):not([tabindex='-1'])")
+        .filter((element) =>
+            element.style.visibility !== "hidden"
+            && element.style.display != "none");
+  }
+}
+
+Listener.register(Focus.init, Phase.DOCUMENT_READY);
+Listener.register(Focus.init, Phase.AFTER_UPDATE);
diff --git a/tobago-theme/tobago-theme-standard/src/main/npm/ts/tobago-in.ts b/tobago-theme/tobago-theme-standard/src/main/npm/ts/tobago-in.ts
index 75f75ff..bade8dc 100644
--- a/tobago-theme/tobago-theme-standard/src/main/npm/ts/tobago-in.ts
+++ b/tobago-theme/tobago-theme-standard/src/main/npm/ts/tobago-in.ts
@@ -15,11 +15,10 @@
  * limitations under the License.
  */
 
-// XXX regexp example only - blueprint
-
 import {Listener, Phase} from "./tobago-listener";
 import {DomUtils} from "./tobago-utils";
 
+// XXX regexp example only - blueprint
 class RegExpTest {
 
   private readonly element: HTMLInputElement;
diff --git a/tobago-theme/tobago-theme-standard/src/main/resources/META-INF/tobago-config.xml b/tobago-theme/tobago-theme-standard/src/main/resources/META-INF/tobago-config.xml
index 2cb11e7..313ee3f 100644
--- a/tobago-theme/tobago-theme-standard/src/main/resources/META-INF/tobago-config.xml
+++ b/tobago-theme/tobago-theme-standard/src/main/resources/META-INF/tobago-config.xml
@@ -73,6 +73,7 @@
           <script name="/tobago/standard/tobago-bootstrap/${project.version}/js/tobago-calendar.js"/>
           <script name="/tobago/standard/tobago-bootstrap/${project.version}/js/tobago-command.js"/>
           <script name="/tobago/standard/tobago-bootstrap/${project.version}/js/tobago-file.js"/>
+          <script name="/tobago/standard/tobago-bootstrap/${project.version}/js/tobago-focus.js"/>
           <script name="/tobago/standard/tobago-bootstrap/${project.version}/js/tobago-header-footer.js"/>
           <script name="/tobago/standard/tobago-bootstrap/${project.version}/js/tobago-in.js"/>
           <script name="/tobago/standard/tobago-bootstrap/${project.version}/js/tobago-jsf.js"/>