| /* |
| * 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 {Page} from "./tobago-page"; |
| import {DomUtils} from "./tobago-utils"; |
| |
| export class Focus extends HTMLElement { |
| |
| /** |
| * The focusListener to set the lastFocusId must be implemented in the appropriate web elements. |
| * @param event |
| */ |
| static setLastFocusId(event: FocusEvent): void { |
| const target = event.target as HTMLElement; |
| let computedStyle = getComputedStyle(target); |
| |
| if (target.getAttribute("type") !== "hidden" |
| && target.getAttributeNames().indexOf("disabled") === -1 |
| && target.getAttribute("tabindex") !== "-1" |
| && computedStyle.visibility !== "hidden" |
| && computedStyle.display !== "none") { |
| const root = target.getRootNode() as ShadowRoot | Document; |
| const tobagoFocus = root.getElementById(Page.page().id + DomUtils.SUB_COMPONENT_SEP + "lastFocusId"); |
| tobagoFocus.querySelector("input").value = target.id; |
| } |
| } |
| |
| constructor() { |
| super(); |
| } |
| |
| /** |
| * 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) |
| */ |
| connectedCallback(): void { |
| const errorElement = this.errorElement; |
| if (errorElement) { |
| errorElement.focus(); |
| return; |
| } |
| |
| if (this.autofocusElements.length > 0) { |
| // nothing to do, because the browser make the work. |
| return; |
| } |
| |
| const lastFocusedElement = this.lastFocusedElement; |
| if (lastFocusedElement) { |
| lastFocusedElement.focus(); |
| return; |
| } |
| |
| const focusableElement = this.focusableElement; |
| if (focusableElement) { |
| focusableElement.focus(); |
| return; |
| } |
| } |
| |
| private get errorElement(): HTMLElement { |
| const root = this.getRootNode() as ShadowRoot | Document; |
| const elements = root.querySelectorAll( |
| ".tobago-messages-container .border-danger:not([disabled]):not([tabindex='-1'])") as NodeListOf<HTMLElement>; |
| |
| for (const element of elements) { |
| const computedStyle = getComputedStyle(element); |
| if (computedStyle.display !== "none" && computedStyle.visibility !== "hidden") { |
| return element; |
| } |
| } |
| } |
| |
| private get autofocusElements(): NodeListOf<HTMLElement> { |
| const root = this.getRootNode() as ShadowRoot | Document; |
| return root.querySelectorAll("[autofocus]"); |
| } |
| |
| private get lastFocusedElement(): HTMLElement { |
| const lastFocusId: string = this.hiddenInput.value; |
| if (lastFocusId) { |
| const root = this.getRootNode() as ShadowRoot | Document; |
| return root.getElementById(lastFocusId); |
| } else { |
| return null; |
| } |
| } |
| |
| private get hiddenInput(): HTMLInputElement { |
| return this.querySelector("input"); |
| } |
| |
| private get focusableElement(): HTMLElement { |
| const root = this.getRootNode() as ShadowRoot | Document; |
| const elements = root.querySelectorAll( |
| "input:not([type='hidden']):not([disabled]):not([tabindex='-1'])," + |
| "select:not([disabled]):not([tabindex='-1'])," + |
| "textarea:not([disabled]):not([tabindex='-1'])") as NodeListOf<HTMLElement>; |
| |
| for (const element of elements) { |
| if (this.isVisible(element)) { |
| return element; |
| } |
| } |
| } |
| |
| private isVisible(element: HTMLElement): boolean { |
| const computedStyle = getComputedStyle(element); |
| if (computedStyle.display === "none" || computedStyle.visibility === "hidden") { |
| return false; |
| } else if (element.parentElement) { |
| return this.isVisible(element.parentElement); |
| } else { |
| return true; |
| } |
| } |
| } |
| |
| document.addEventListener("DOMContentLoaded", function (event: Event): void { |
| window.customElements.define("tobago-focus", Focus); |
| }); |