Vmeda.Online/src/directives/keep-keyboard.ts

178 lines
7.1 KiB
TypeScript
Raw Normal View History

// (C) Copyright 2015 Martin Dougiamas
//
// Licensed 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 { Directive, AfterViewInit, Input, ElementRef, OnDestroy } from '@angular/core';
import { CoreDomUtilsProvider } from '@providers/utils/dom';
import { CoreUtilsProvider } from '@providers/utils/utils';
/**
* IMPORTANT: This directive is deprecated because it causes a weird effect (the keyboard closes and opens again).
* We recommend using core-suppress-events directive for a better user experience.
*
* Directive to keep the keyboard open when clicking a certain element (usually a button).
*
* @description
*
* This directive needs to be applied to an input or textarea. The value of the directive needs to be a selector
* to identify the element to listen for clicks (usually a button).
*
* When that element is clicked, the input that has this directive will keep the focus if it has it already and the keyboard
* won't be closed.
*
* Example usage:
*
2017-12-29 17:05:52 +00:00
* <textarea [core-keep-keyboard]="'#mma-messages-send-message-button'"></textarea>
* <button id="mma-messages-send-message-button">Send</button>
*
* Alternatively, this directive can be applied to the button. The value of the directive needs to be a selector to identify
* the input element. In this case, you need to set [inButton]="true".
*
* Example usage:
*
* <textarea id="send-message-input"></textarea>
2017-12-29 17:05:52 +00:00
* <button [core-keep-keyboard]="'#send-message-input'" [inButton]="true">Send</button>
*
* @deprecated v3.5.2
*/
@Directive({
selector: '[core-keep-keyboard]'
})
export class CoreKeepKeyboardDirective implements AfterViewInit, OnDestroy {
@Input('core-keep-keyboard') selector: string; // Selector to identify the button or input.
2018-01-29 09:05:20 +00:00
@Input() inButton?: boolean | string; // Whether this directive is applied to the button (true) or to the input (false).
protected element: HTMLElement; // Current element.
protected button: HTMLElement; // Button element.
protected input: HTMLElement; // Input element.
protected lastFocusOut = 0; // Last time the input was focused out.
2018-01-29 09:05:20 +00:00
protected clickListener: any; // Listener for clicks in the button.
protected focusOutListener: any; // Listener for focusout in the input.
protected focusAgainListener: any; // Another listener for focusout, with the purpose to focus again.
protected stopFocusAgainTimeout: any; // Timeout to stop focus again listener.
constructor(element: ElementRef, private domUtils: CoreDomUtilsProvider, private utils: CoreUtilsProvider) {
this.element = element.nativeElement;
}
/**
* View has been initialized.
*/
2018-01-29 09:05:20 +00:00
ngAfterViewInit(): void {
// Use a setTimeout because to make sure that child components have been treated.
setTimeout(() => {
2018-01-29 09:05:20 +00:00
const inButton = this.utils.isTrueOrOne(this.inButton);
let candidateEls,
selectedEl;
if (typeof this.selector != 'string' || !this.selector) {
// Not a valid selector, stop.
return;
}
// Get the selected element. Get the last one found.
candidateEls = document.querySelectorAll(this.selector);
selectedEl = candidateEls[candidateEls.length - 1];
if (!selectedEl) {
// Element not found.
return;
}
if (inButton) {
// The directive is applied to the button.
this.button = this.element;
this.input = selectedEl;
} else {
// The directive is applied to the input.
this.button = selectedEl;
if (this.element.tagName == 'ION-INPUT') {
// Search the inner input.
this.input = this.element.querySelector('input');
} else if (this.element.tagName == 'ION-TEXTAREA') {
// Search the inner textarea.
this.input = this.element.querySelector('textarea');
} else {
this.input = this.element;
}
if (!this.input) {
// Input not found, stop.
return;
}
}
// Listen for focusout event. This is to be able to check if previous focus was on this element.
this.focusOutListener = this.focusOut.bind(this);
this.input.addEventListener('focusout', this.focusOutListener);
// Listen for clicks in the button.
this.clickListener = this.buttonClicked.bind(this);
this.button.addEventListener('click', this.clickListener);
});
}
/**
* Component destroyed.
*/
2018-01-29 09:05:20 +00:00
ngOnDestroy(): void {
if (this.button && this.clickListener) {
this.button.removeEventListener('click', this.clickListener);
}
if (this.input && this.focusOutListener) {
this.input.removeEventListener('focusout', this.focusOutListener);
}
}
/**
* The button we're interested in was clicked.
*/
2018-01-29 09:05:20 +00:00
protected buttonClicked(): void {
if (document.activeElement == this.input) {
// Directive's element is focused at the time the button is clicked. Listen for focusout to focus it again.
this.focusAgainListener = this.focusElementAgain.bind(this);
this.input.addEventListener('focusout', this.focusAgainListener);
} else if (document.activeElement == this.button && Date.now() - this.lastFocusOut < 200) {
// Last focused element was the directive's element, focus it again.
setTimeout(this.focusElementAgain.bind(this), 0);
}
}
/**
* If keyboard is open, focus the input again and stop listening focusout to focus again if needed.
*/
2018-01-29 09:05:20 +00:00
protected focusElementAgain(): void {
this.domUtils.focusElement(this.input);
if (this.focusAgainListener) {
2018-01-29 09:05:20 +00:00
// Sometimes we can receive more than 1 focus out event.
// If we spend 1 second without receiving any, stop listening for them.
const listener = this.focusAgainListener; // Store it in a local variable, in case it changes.
clearTimeout(this.stopFocusAgainTimeout);
this.stopFocusAgainTimeout = setTimeout(() => {
this.input.removeEventListener('focusout', listener);
if (listener == this.focusAgainListener) {
delete this.focusAgainListener;
}
}, 1000);
}
}
/**
* Input was focused out, save the time it was done.
*/
2018-01-29 09:05:20 +00:00
protected focusOut(): void {
this.lastFocusOut = Date.now();
}
}