9e92e547377189d674c59cdce2ced3f21f97c565
[Packages/TYPO3.CMS.git] / Build / Sources / TypeScript / backend / Resources / Public / TypeScript / Input / Clearable.ts
1 /*
2  * This file is part of the TYPO3 CMS project.
3  *
4  * It is free software; you can redistribute it and/or modify it under
5  * the terms of the GNU General Public License, either version 2
6  * of the License, or any later version.
7  *
8  * For the full copyright and license information, please read the
9  * LICENSE.txt file that was distributed with this source code.
10  *
11  * The TYPO3 project - inspiring people to share!
12  */
13
14 class Clearable {
15   private static createCloseButton(): HTMLButtonElement {
16     // The inlined markup represents the current generated markup from the
17     // icon api for the icon actions-close that can be found in the official
18     // icon repository and is registered in the backend icon api.
19     //
20     // It´s not possible to use/open the backend icon api without opening
21     // new possible vectors for attackers to sniff system information.
22     //
23     // When the icon definition of actions-close changes also the inline
24     // icon should be updated.
25     //
26     // https://github.com/typo3/typo3.icons
27     const closeIcon =
28       `<span class="t3js-icon icon icon-size-small icon-state-default icon-actions-close" data-identifier="actions-close">
29         <span class="icon-markup">
30             <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16">
31                 <path
32                     d="M11.9 5.5L9.4 8l2.5 2.5c.2.2.2.5 0
33                     .7l-.7.7c-.2.2-.5.2-.7 0L8 9.4l-2.5 2.5c-.2.2-.5.2-.7
34                     0l-.7-.7c-.2-.2-.2-.5 0-.7L6.6 8 4.1 5.5c-.2-.2-.2-.5
35                     0-.7l.7-.7c.2-.2.5-.2.7 0L8 6.6l2.5-2.5c.2-.2.5-.2.7
36                     0l.7.7c.2.2.2.5 0 .7z"
37                     class="icon-color"/>
38             </svg>
39         </span>
40     </span>`;
41
42     const closeButton = document.createElement('button');
43     closeButton.type = 'button';
44     closeButton.innerHTML = closeIcon;
45     closeButton.style.visibility = 'hidden';
46     closeButton.classList.add('close');
47
48     return closeButton;
49   }
50
51   constructor() {
52     if (typeof HTMLInputElement.prototype.clearable === 'function') {
53       return;
54     }
55
56     this.registerClearable();
57   }
58
59   private registerClearable(): void {
60     HTMLInputElement.prototype.clearable = function(options: Options = {}): void {
61       if (this.isClearable) {
62         // input field is already clearable, nothing to do here
63         return;
64       }
65
66       if (typeof options !== 'object') {
67         throw new Error('Passed options must be an object, ' + typeof options + ' given');
68       }
69
70       const wrap = document.createElement('div');
71       wrap.classList.add('form-control-clearable');
72       this.parentNode.insertBefore(wrap, this);
73       wrap.appendChild(this);
74
75       const clearButton = Clearable.createCloseButton();
76       const toggleClearButtonVisibility = (): void => {
77         clearButton.style.visibility = this.value.length === 0 ? 'hidden' : 'visible';
78       };
79
80       clearButton.addEventListener('click', (e: Event): void => {
81         e.preventDefault();
82
83         this.value = '';
84
85         if (typeof options.onClear === 'function') {
86           options.onClear(this);
87         }
88
89         this.dispatchEvent(new Event('change', {bubbles: true, cancelable: true}));
90         toggleClearButtonVisibility();
91       });
92       wrap.appendChild(clearButton);
93
94       this.addEventListener('focus', toggleClearButtonVisibility);
95       this.addEventListener('keyup', toggleClearButtonVisibility);
96
97       toggleClearButtonVisibility();
98       this.isClearable = true;
99     };
100   }
101 }
102
103 export = new Clearable();