@luftborn/custom-elements 1.4.0 → 1.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (196) hide show
  1. package/README.md +2 -2
  2. package/assets/style.css +277 -277
  3. package/azure-pipeline.yml +54 -0
  4. package/demo/index.html +585 -585
  5. package/demo/index.js +6109 -0
  6. package/demo/index.min.js +6110 -0
  7. package/demo/index.min.js.map +1 -0
  8. package/demo/index.ts +11 -11
  9. package/demo/old/index.html +586 -586
  10. package/demo/old/index.js +10849 -10849
  11. package/demo/old/index.min.js +10850 -10850
  12. package/demo/old/index.ts +10 -10
  13. package/dist/custom-form.d.ts +20 -20
  14. package/dist/custom-form.js +147 -147
  15. package/dist/elements/Address/AddressElement.d.ts +28 -28
  16. package/dist/elements/Address/AddressElement.js +206 -206
  17. package/dist/elements/BankField/BankFieldElement.d.ts +22 -22
  18. package/dist/elements/BankField/BankFieldElement.js +127 -127
  19. package/dist/elements/CPRElement/CPRElement.d.ts +14 -14
  20. package/dist/elements/CPRElement/CPRElement.js +82 -82
  21. package/dist/elements/CVRElement/CVRElement.d.ts +14 -14
  22. package/dist/elements/CVRElement/CVRElement.js +82 -82
  23. package/dist/elements/CheckBoxElement/CheckBoxElement.d.ts +14 -14
  24. package/dist/elements/CheckBoxElement/CheckBoxElement.js +96 -96
  25. package/dist/elements/CustomRegularExpression/CustomRegularExpressionElement.d.ts +12 -12
  26. package/dist/elements/CustomRegularExpression/CustomRegularExpressionElement.js +82 -82
  27. package/dist/elements/DateField/DateFieldElement.d.ts +12 -12
  28. package/dist/elements/DateField/DateFieldElement.js +85 -85
  29. package/dist/elements/DropDownList/DropDownListElement.d.ts +14 -14
  30. package/dist/elements/DropDownList/DropDownListElement.js +93 -93
  31. package/dist/elements/Elements.d.ts +18 -18
  32. package/dist/elements/Elements.js +37 -37
  33. package/dist/elements/EmailField/EmailFieldElement.d.ts +13 -13
  34. package/dist/elements/EmailField/EmailFieldElement.js +83 -83
  35. package/dist/elements/FileField/FileFieldElement.d.ts +15 -15
  36. package/dist/elements/FileField/FileFieldElement.js +103 -103
  37. package/dist/elements/IdentificationElement/IdentificationElement.d.ts +18 -18
  38. package/dist/elements/IdentificationElement/IdentificationElement.js +94 -94
  39. package/dist/elements/InternationaPhoneNumber/InternationaPhoneNumberElement.d.ts +16 -16
  40. package/dist/elements/InternationaPhoneNumber/InternationaPhoneNumberElement.js +114 -114
  41. package/dist/elements/NumericField/NumericFieldElement.d.ts +12 -12
  42. package/dist/elements/NumericField/NumericFieldElement.js +91 -91
  43. package/dist/elements/RadioButtonGroup/RadioButtonGroupElement.d.ts +14 -14
  44. package/dist/elements/RadioButtonGroup/RadioButtonGroupElement.js +99 -99
  45. package/dist/elements/TextAreaElement/TextAreaElement.d.ts +12 -12
  46. package/dist/elements/TextAreaElement/TextAreaElement.js +79 -79
  47. package/dist/elements/TextField/TextFieldElement.d.ts +12 -12
  48. package/dist/elements/TextField/TextFieldElement.js +79 -79
  49. package/dist/elements/TypeAhead/TypeAheadElement.d.ts +24 -24
  50. package/dist/elements/TypeAhead/TypeAheadElement.js +209 -209
  51. package/dist/framework/CustomEvents.d.ts +21 -21
  52. package/dist/framework/CustomEvents.js +47 -47
  53. package/dist/framework/CustomInputElement.d.ts +50 -50
  54. package/dist/framework/CustomInputElement.js +258 -258
  55. package/dist/framework/Language/Languages/Base/BaseDictionary.d.ts +4 -4
  56. package/dist/framework/Language/Languages/Base/BaseDictionary.js +20 -20
  57. package/dist/framework/Language/Languages/CzechDictionary.d.ts +4 -4
  58. package/dist/framework/Language/Languages/CzechDictionary.js +39 -39
  59. package/dist/framework/Language/Languages/DanishDictionary.d.ts +4 -4
  60. package/dist/framework/Language/Languages/DanishDictionary.js +39 -39
  61. package/dist/framework/Language/Languages/DutchDictionary.d.ts +4 -4
  62. package/dist/framework/Language/Languages/DutchDictionary.js +39 -39
  63. package/dist/framework/Language/Languages/EnglishDictionary.d.ts +4 -4
  64. package/dist/framework/Language/Languages/EnglishDictionary.js +39 -39
  65. package/dist/framework/Language/Languages/EstonianDictionary.d.ts +4 -4
  66. package/dist/framework/Language/Languages/EstonianDictionary.js +39 -39
  67. package/dist/framework/Language/Languages/FinnishDictionary.d.ts +4 -4
  68. package/dist/framework/Language/Languages/FinnishDictionary.js +39 -39
  69. package/dist/framework/Language/Languages/FrenchDictionary.d.ts +4 -4
  70. package/dist/framework/Language/Languages/FrenchDictionary.js +39 -39
  71. package/dist/framework/Language/Languages/GermanDictionary.d.ts +4 -4
  72. package/dist/framework/Language/Languages/GermanDictionary.js +39 -39
  73. package/dist/framework/Language/Languages/GreekDictionary.d.ts +4 -4
  74. package/dist/framework/Language/Languages/GreekDictionary.js +39 -39
  75. package/dist/framework/Language/Languages/HungarianDictionary.d.ts +4 -4
  76. package/dist/framework/Language/Languages/HungarianDictionary.js +39 -39
  77. package/dist/framework/Language/Languages/IcelandicDictionary.d.ts +4 -4
  78. package/dist/framework/Language/Languages/IcelandicDictionary.js +39 -39
  79. package/dist/framework/Language/Languages/ItalianDictionary.d.ts +4 -4
  80. package/dist/framework/Language/Languages/ItalianDictionary.js +39 -39
  81. package/dist/framework/Language/Languages/LatvianDictionary.d.ts +4 -4
  82. package/dist/framework/Language/Languages/LatvianDictionary.js +39 -39
  83. package/dist/framework/Language/Languages/LituanianDictionary.d.ts +4 -4
  84. package/dist/framework/Language/Languages/LituanianDictionary.js +39 -39
  85. package/dist/framework/Language/Languages/NorwegianDictionary.d.ts +4 -4
  86. package/dist/framework/Language/Languages/NorwegianDictionary.js +39 -39
  87. package/dist/framework/Language/Languages/PolishDictionary.d.ts +4 -4
  88. package/dist/framework/Language/Languages/PolishDictionary.js +39 -39
  89. package/dist/framework/Language/Languages/PortugueseDictionary.d.ts +4 -4
  90. package/dist/framework/Language/Languages/PortugueseDictionary.js +39 -39
  91. package/dist/framework/Language/Languages/SpanishDictionary.d.ts +4 -4
  92. package/dist/framework/Language/Languages/SpanishDictionary.js +39 -39
  93. package/dist/framework/Language/Languages/SwedishDictionary.d.ts +4 -4
  94. package/dist/framework/Language/Languages/SwedishDictionary.js +39 -39
  95. package/dist/framework/Language/Translator.d.ts +23 -23
  96. package/dist/framework/Language/Translator.js +115 -115
  97. package/dist/framework/Models/OptionWithDescription.d.ts +6 -6
  98. package/dist/framework/Models/OptionWithDescription.js +9 -9
  99. package/dist/framework/Polyfills/getAttributeNamesPolyfill.d.ts +1 -1
  100. package/dist/framework/Polyfills/getAttributeNamesPolyfill.js +17 -17
  101. package/dist/framework/Translations/CaseConverter.d.ts +5 -5
  102. package/dist/framework/Translations/CaseConverter.js +50 -50
  103. package/dist/framework/Utilities/ArrayUtil.d.ts +6 -6
  104. package/dist/framework/Utilities/ArrayUtil.js +36 -36
  105. package/dist/framework/Utilities/DomUtil.d.ts +21 -21
  106. package/dist/framework/Utilities/DomUtil.js +101 -101
  107. package/dist/framework/Utilities/DomUtility.d.ts +3 -3
  108. package/dist/framework/Utilities/DomUtility.js +12 -12
  109. package/dist/framework/Utilities/MakeRequest.d.ts +8 -8
  110. package/dist/framework/Utilities/MakeRequest.js +48 -48
  111. package/dist/framework/Utilities/StringUtil.d.ts +4 -4
  112. package/dist/framework/Utilities/StringUtil.js +16 -16
  113. package/dist/framework/Utilities/ViewportUtil.d.ts +8 -8
  114. package/dist/framework/Utilities/ViewportUtil.js +42 -42
  115. package/dist/framework/Utilities/debouncer.d.ts +8 -8
  116. package/dist/framework/Utilities/debouncer.js +51 -51
  117. package/dist/framework/Validation/IValidator.interface.d.ts +3 -3
  118. package/dist/framework/Validation/IValidator.interface.js +2 -2
  119. package/dist/framework/Validation/Validators/BankAccount.d.ts +4 -4
  120. package/dist/framework/Validation/Validators/BankAccount.js +12 -12
  121. package/dist/framework/Validation/Validators/BankAccountRegistrationNumber.d.ts +4 -4
  122. package/dist/framework/Validation/Validators/BankAccountRegistrationNumber.js +12 -12
  123. package/dist/framework/Validation/Validators/BankIdNorway.d.ts +5 -5
  124. package/dist/framework/Validation/Validators/BankIdNorway.js +21 -21
  125. package/dist/framework/Validation/Validators/BankIdSweden.d.ts +5 -5
  126. package/dist/framework/Validation/Validators/BankIdSweden.js +21 -21
  127. package/dist/framework/Validation/Validators/CPR.d.ts +9 -9
  128. package/dist/framework/Validation/Validators/CPR.js +75 -75
  129. package/dist/framework/Validation/Validators/CVR.d.ts +5 -5
  130. package/dist/framework/Validation/Validators/CVR.js +30 -30
  131. package/dist/framework/custom-element.decorator.d.ts +9 -9
  132. package/dist/framework/custom-element.decorator.js +51 -51
  133. package/dist/index.d.ts +7 -7
  134. package/dist/index.js +14 -14
  135. package/package.json +38 -38
  136. package/src/custom-form.ts +140 -140
  137. package/src/elements/Address/AddressElement.ts +216 -216
  138. package/src/elements/BankField/BankFieldElement.ts +127 -127
  139. package/src/elements/CPRElement/CPRElement.ts +74 -74
  140. package/src/elements/CVRElement/CVRElement.ts +75 -75
  141. package/src/elements/CheckBoxElement/CheckBoxElement.ts +99 -99
  142. package/src/elements/CustomRegularExpression/CustomRegularExpressionElement.ts +75 -75
  143. package/src/elements/DateField/DateFieldElement.ts +78 -78
  144. package/src/elements/DropDownList/DropDownListElement.ts +100 -100
  145. package/src/elements/Elements.ts +37 -37
  146. package/src/elements/EmailField/EmailFieldElement.ts +78 -78
  147. package/src/elements/FileField/FileFieldElement.ts +96 -96
  148. package/src/elements/IdentificationElement/IdentificationElement.ts +104 -104
  149. package/src/elements/InternationaPhoneNumber/InternationaPhoneNumberElement.ts +116 -116
  150. package/src/elements/NumericField/NumericFieldElement.ts +84 -84
  151. package/src/elements/RadioButtonGroup/RadioButtonGroupElement.ts +103 -103
  152. package/src/elements/TextAreaElement/TextAreaElement.ts +72 -72
  153. package/src/elements/TextField/TextFieldElement.ts +72 -72
  154. package/src/elements/TypeAhead/TypeAheadElement.ts +248 -248
  155. package/src/framework/CustomEvents.ts +47 -47
  156. package/src/framework/CustomInputElement.ts +277 -277
  157. package/src/framework/Language/Languages/Base/BaseDictionary.ts +15 -15
  158. package/src/framework/Language/Languages/CzechDictionary.ts +18 -18
  159. package/src/framework/Language/Languages/DanishDictionary.ts +17 -17
  160. package/src/framework/Language/Languages/DutchDictionary.ts +17 -17
  161. package/src/framework/Language/Languages/EnglishDictionary.ts +18 -18
  162. package/src/framework/Language/Languages/EstonianDictionary.ts +17 -17
  163. package/src/framework/Language/Languages/FinnishDictionary.ts +17 -17
  164. package/src/framework/Language/Languages/FrenchDictionary.ts +17 -17
  165. package/src/framework/Language/Languages/GermanDictionary.ts +17 -17
  166. package/src/framework/Language/Languages/GreekDictionary.ts +17 -17
  167. package/src/framework/Language/Languages/HungarianDictionary.ts +17 -17
  168. package/src/framework/Language/Languages/IcelandicDictionary.ts +17 -17
  169. package/src/framework/Language/Languages/ItalianDictionary.ts +17 -17
  170. package/src/framework/Language/Languages/LatvianDictionary.ts +17 -17
  171. package/src/framework/Language/Languages/LituanianDictionary.ts +17 -17
  172. package/src/framework/Language/Languages/NorwegianDictionary.ts +17 -17
  173. package/src/framework/Language/Languages/PolishDictionary.ts +17 -17
  174. package/src/framework/Language/Languages/PortugueseDictionary.ts +17 -17
  175. package/src/framework/Language/Languages/SpanishDictionary.ts +17 -17
  176. package/src/framework/Language/Languages/SwedishDictionary.ts +17 -17
  177. package/src/framework/Language/Translator.ts +110 -110
  178. package/src/framework/Models/OptionWithDescription.ts +8 -8
  179. package/src/framework/Polyfills/getAttributeNamesPolyfill.ts +13 -13
  180. package/src/framework/Translations/CaseConverter.ts +43 -43
  181. package/src/framework/Utilities/ArrayUtil.ts +31 -31
  182. package/src/framework/Utilities/DomUtil.ts +117 -117
  183. package/src/framework/Utilities/DomUtility.ts +6 -6
  184. package/src/framework/Utilities/MakeRequest.ts +47 -47
  185. package/src/framework/Utilities/StringUtil.ts +8 -8
  186. package/src/framework/Utilities/ViewportUtil.ts +46 -46
  187. package/src/framework/Utilities/debouncer.ts +66 -66
  188. package/src/framework/Validation/IValidator.interface.ts +3 -3
  189. package/src/framework/Validation/Validators/BankAccount.ts +7 -7
  190. package/src/framework/Validation/Validators/BankAccountRegistrationNumber.ts +8 -8
  191. package/src/framework/Validation/Validators/BankIdNorway.ts +21 -21
  192. package/src/framework/Validation/Validators/BankIdSweden.ts +21 -21
  193. package/src/framework/Validation/Validators/CPR.ts +82 -82
  194. package/src/framework/Validation/Validators/CVR.ts +27 -27
  195. package/src/framework/custom-element.decorator.ts +57 -57
  196. package/src/index.ts +12 -12
@@ -1,248 +1,248 @@
1
- import CustomElement from '../../framework/custom-element.decorator';
2
- import { CustomInputElement } from '../../framework/CustomInputElement';
3
- import { CustomElementEventArgs } from '../../framework/CustomEvents';
4
- import OptionWithDescription from '../../framework/Models/OptionWithDescription';
5
- import DomUtility from '../../framework/Utilities/DomUtility';
6
-
7
- @CustomElement({
8
- selector: 'type-ahead-element',
9
- template: `
10
- <div class="wrapper">
11
- <input type="text" id="text-input">
12
- <div id="description"></div>
13
- </div>`,
14
- style: `
15
- :host {
16
- width:100%;
17
- }
18
- .wrapper {
19
- position: relative;
20
- display:flex;
21
- flex-direction: column;
22
- justify-content: center;
23
- align-items: center;
24
- }
25
- #text-input {
26
- box-sizing: border-box;
27
- width: 100% !important;
28
- border: none;
29
- background-color: #f1f4ff;
30
- margin: 2px;
31
- resize: none;
32
- }
33
- .wrapper #description {
34
- width: 100%;
35
- padding: 0 2px;
36
- box-sizing: border-box;
37
- }
38
- .options-list {
39
- position: absolute;
40
- border: 1px solid #E8E8E8;
41
- border-bottom: none;
42
- border-top: none;
43
- z-index: 3;
44
- top: 100%;
45
- left: 0;
46
- right: 0;
47
- max-height: 200px;
48
- overflow-y: auto;
49
- }
50
- .options-list-item {
51
- padding: 10px;
52
- cursor: pointer;
53
- background-color: #FFFFFF;
54
- border-bottom: 1px solid #E8E8E8;
55
- }
56
- .options-list-item:hover {
57
- background-color: #E8E8E8;
58
- }
59
- .options-list-item.active {
60
- /*when navigating through the items using the arrow keys:*/
61
- background-color: #003E64 !important;
62
- color: #FFFFFF;
63
- }
64
- .options-list-item-title {
65
- font-size: 16px;
66
- }
67
- .options-list-item-description {
68
- font-size: 10px;
69
- }
70
- `,
71
- useShadow: true,
72
- })
73
- export class TypeAheadElement extends CustomInputElement {
74
-
75
- private textInputElement: HTMLInputElement;
76
- private descriptionElement: HTMLElement;
77
- private componentWrapper: HTMLElement;
78
- private currentFoucsedIndex = -1;
79
-
80
- selectedOption: OptionWithDescription;
81
- searchResultOptions: OptionWithDescription[] = [];
82
-
83
- constructor() {
84
- super();
85
- }
86
-
87
- private get searchHasResults(): boolean {
88
- const autocompleteList = super.getChildElement('.options-list');
89
- return autocompleteList && this.optionsWithDescriptions && this.optionsWithDescriptions.length > 0;
90
- }
91
-
92
- get value(): string {
93
- if (!this.selectedOption) {
94
- return null;
95
- }
96
- return this.selectedOption.value;
97
- }
98
-
99
- set value(value: string) {
100
- if (!this.optionsWithDescriptions || this.optionsWithDescriptions.length === 0) {
101
- return;
102
- }
103
- const option = this.optionsWithDescriptions.find(option => option.value === value);
104
- if (!option) {
105
- this.descriptionElement.innerHTML = null;
106
- return;
107
- }
108
- this.selectedOption = option;
109
- this.textInputElement.value = option.title;
110
- this.descriptionElement.innerHTML = option.description;
111
- }
112
-
113
- get valid(): boolean {
114
- if (this.required) {
115
- return !!this.selectedOption;
116
- }
117
- return !!this.selectedOption || this.textInputElement.value === '';
118
- }
119
-
120
- connectedCallback(): void {
121
- super.connectedCallback();
122
- }
123
-
124
- initChildInputs() {
125
- this.textInputElement = super.getChildInput('#text-input');
126
- this.descriptionElement = super.getChildInput('#description');
127
- this.componentWrapper = super.getChildElement('.wrapper');
128
- if (this.required) {
129
- this.textInputElement.setAttribute('required', '');
130
- }
131
- this.textInputElement.addEventListener('change', this.change.bind(this));
132
- this.textInputElement.addEventListener('input', () => {
133
- this.inputEventHandler();
134
- });
135
- this.bindNavigation();
136
- }
137
-
138
- private inputEventHandler() {
139
- this.selectedOption = null;
140
- this.descriptionElement.innerHTML = null;
141
- this.closeOptionsList();
142
- this.searchResultOptions = [];
143
- const searchValue = this.textInputElement.value;
144
- if (!searchValue || searchValue.trim() === '') {
145
- return;
146
- }
147
- const optionslist = document.createElement('div');
148
- optionslist.classList.add('options-list');
149
-
150
- if (!this.optionsWithDescriptions) {
151
- return;
152
- }
153
- this.optionsWithDescriptions.forEach((option: OptionWithDescription, index) => {
154
- var regex = new RegExp(searchValue, 'gi');
155
- const isMatchingTitle = regex.test(option.title);
156
- const isMatchingValue = regex.test(option.value);
157
- const isMatchingDescription = regex.test(option.description);
158
- if (!isMatchingTitle && !isMatchingValue && !isMatchingDescription) {
159
- return;
160
- }
161
- this.searchResultOptions.push(option);
162
- const optionsListItem = document.createElement("div");
163
- optionsListItem.classList.add('options-list-item');
164
- optionsListItem.dataset['value'] = option.value;
165
- optionslist.appendChild(optionsListItem);
166
- const optionsListItemTitle = document.createElement("div");
167
- optionsListItemTitle.classList.add('options-list-item-title');
168
- optionsListItemTitle.innerHTML = DomUtility.boldenText(option.title, searchValue);
169
- optionsListItem.appendChild(optionsListItemTitle);
170
- const optionsListItemDescription = document.createElement("div");
171
- optionsListItemDescription.classList.add('options-list-item-description');
172
- optionsListItemDescription.innerHTML = DomUtility.boldenText(option.description, searchValue);
173
- optionsListItem.appendChild(optionsListItemDescription);
174
- optionsListItem.addEventListener('click', () => {
175
- this.optionSelectedHandler(option);
176
- });
177
- this.componentWrapper.appendChild(optionslist);
178
- });
179
- }
180
-
181
- private closeOptionsList() {
182
- this.currentFoucsedIndex = -1;
183
- const optionsList = super.getChildElement('.options-list');
184
- if (!optionsList) {
185
- return;
186
- }
187
- optionsList.remove();
188
- }
189
-
190
- private optionSelectedHandler(option: OptionWithDescription) {
191
- this.value = option.value;
192
- this.onChange.emit(new CustomElementEventArgs(this.value, 'change'));
193
- this.closeOptionsList();
194
- }
195
-
196
- private bindNavigation() {
197
- this.textInputElement.addEventListener('keydown', (event) => {
198
- if (event.key === 'Escape') {
199
- this.closeOptionsList();
200
- }
201
- if (event.key === 'ArrowDown') {
202
- if (!this.searchHasResults) {
203
- return;
204
- }
205
- this.currentFoucsedIndex = this.currentFoucsedIndex === this.searchResultOptions.length - 1 ? 0 : this.currentFoucsedIndex + 1;
206
- this.highlightFocusedSearchResult();
207
- }
208
- if (event.key === 'ArrowUp') {
209
- if (!this.searchHasResults) {
210
- return;
211
- }
212
- this.currentFoucsedIndex = this.currentFoucsedIndex === 0 ? this.searchResultOptions.length - 1 : this.currentFoucsedIndex - 1;
213
- this.highlightFocusedSearchResult();
214
- }
215
- if (event.key === 'Enter') {
216
- if (this.currentFoucsedIndex !== -1 && this.searchHasResults) {
217
- this.optionSelectedHandler(this.searchResultOptions[this.currentFoucsedIndex]);
218
- return;
219
- }
220
- }
221
- });
222
- }
223
-
224
- highlightFocusedSearchResult() {
225
- const optionListItems = super.getChildElements('.options-list-item');
226
- if (!optionListItems) {
227
- return;
228
- }
229
- optionListItems.forEach((listItem, index) => {
230
- listItem.classList.remove('active');
231
- if (index === this.currentFoucsedIndex) {
232
- listItem.classList.add('active');
233
- }
234
- });
235
- }
236
-
237
- public change($event): void {
238
- this.touched = true;
239
- this.onChange.emit(new CustomElementEventArgs(this.value, 'change'));
240
- }
241
-
242
- public validate(): void {
243
- this.valid;
244
- this.onValidate.emit(
245
- new CustomElementEventArgs(this.value, 'validate'),
246
- );
247
- }
248
- }
1
+ import CustomElement from '../../framework/custom-element.decorator';
2
+ import { CustomInputElement } from '../../framework/CustomInputElement';
3
+ import { CustomElementEventArgs } from '../../framework/CustomEvents';
4
+ import OptionWithDescription from '../../framework/Models/OptionWithDescription';
5
+ import DomUtility from '../../framework/Utilities/DomUtility';
6
+
7
+ @CustomElement({
8
+ selector: 'type-ahead-element',
9
+ template: `
10
+ <div class="wrapper">
11
+ <input type="text" id="text-input">
12
+ <div id="description"></div>
13
+ </div>`,
14
+ style: `
15
+ :host {
16
+ width:100%;
17
+ }
18
+ .wrapper {
19
+ position: relative;
20
+ display:flex;
21
+ flex-direction: column;
22
+ justify-content: center;
23
+ align-items: center;
24
+ }
25
+ #text-input {
26
+ box-sizing: border-box;
27
+ width: 100% !important;
28
+ border: none;
29
+ background-color: #f1f4ff;
30
+ margin: 2px;
31
+ resize: none;
32
+ }
33
+ .wrapper #description {
34
+ width: 100%;
35
+ padding: 0 2px;
36
+ box-sizing: border-box;
37
+ }
38
+ .options-list {
39
+ position: absolute;
40
+ border: 1px solid #E8E8E8;
41
+ border-bottom: none;
42
+ border-top: none;
43
+ z-index: 3;
44
+ top: 100%;
45
+ left: 0;
46
+ right: 0;
47
+ max-height: 200px;
48
+ overflow-y: auto;
49
+ }
50
+ .options-list-item {
51
+ padding: 10px;
52
+ cursor: pointer;
53
+ background-color: #FFFFFF;
54
+ border-bottom: 1px solid #E8E8E8;
55
+ }
56
+ .options-list-item:hover {
57
+ background-color: #E8E8E8;
58
+ }
59
+ .options-list-item.active {
60
+ /*when navigating through the items using the arrow keys:*/
61
+ background-color: #003E64 !important;
62
+ color: #FFFFFF;
63
+ }
64
+ .options-list-item-title {
65
+ font-size: 16px;
66
+ }
67
+ .options-list-item-description {
68
+ font-size: 10px;
69
+ }
70
+ `,
71
+ useShadow: true,
72
+ })
73
+ export class TypeAheadElement extends CustomInputElement {
74
+
75
+ private textInputElement: HTMLInputElement;
76
+ private descriptionElement: HTMLElement;
77
+ private componentWrapper: HTMLElement;
78
+ private currentFoucsedIndex = -1;
79
+
80
+ selectedOption: OptionWithDescription;
81
+ searchResultOptions: OptionWithDescription[] = [];
82
+
83
+ constructor() {
84
+ super();
85
+ }
86
+
87
+ private get searchHasResults(): boolean {
88
+ const autocompleteList = super.getChildElement('.options-list');
89
+ return autocompleteList && this.optionsWithDescriptions && this.optionsWithDescriptions.length > 0;
90
+ }
91
+
92
+ get value(): string {
93
+ if (!this.selectedOption) {
94
+ return null;
95
+ }
96
+ return this.selectedOption.value;
97
+ }
98
+
99
+ set value(value: string) {
100
+ if (!this.optionsWithDescriptions || this.optionsWithDescriptions.length === 0) {
101
+ return;
102
+ }
103
+ const option = this.optionsWithDescriptions.find(option => option.value === value);
104
+ if (!option) {
105
+ this.descriptionElement.innerHTML = null;
106
+ return;
107
+ }
108
+ this.selectedOption = option;
109
+ this.textInputElement.value = option.title;
110
+ this.descriptionElement.innerHTML = option.description;
111
+ }
112
+
113
+ get valid(): boolean {
114
+ if (this.required) {
115
+ return !!this.selectedOption;
116
+ }
117
+ return !!this.selectedOption || this.textInputElement.value === '';
118
+ }
119
+
120
+ connectedCallback(): void {
121
+ super.connectedCallback();
122
+ }
123
+
124
+ initChildInputs() {
125
+ this.textInputElement = super.getChildInput('#text-input');
126
+ this.descriptionElement = super.getChildInput('#description');
127
+ this.componentWrapper = super.getChildElement('.wrapper');
128
+ if (this.required) {
129
+ this.textInputElement.setAttribute('required', '');
130
+ }
131
+ this.textInputElement.addEventListener('change', this.change.bind(this));
132
+ this.textInputElement.addEventListener('input', () => {
133
+ this.inputEventHandler();
134
+ });
135
+ this.bindNavigation();
136
+ }
137
+
138
+ private inputEventHandler() {
139
+ this.selectedOption = null;
140
+ this.descriptionElement.innerHTML = null;
141
+ this.closeOptionsList();
142
+ this.searchResultOptions = [];
143
+ const searchValue = this.textInputElement.value;
144
+ if (!searchValue || searchValue.trim() === '') {
145
+ return;
146
+ }
147
+ const optionslist = document.createElement('div');
148
+ optionslist.classList.add('options-list');
149
+
150
+ if (!this.optionsWithDescriptions) {
151
+ return;
152
+ }
153
+ this.optionsWithDescriptions.forEach((option: OptionWithDescription, index) => {
154
+ var regex = new RegExp(searchValue, 'gi');
155
+ const isMatchingTitle = regex.test(option.title);
156
+ const isMatchingValue = regex.test(option.value);
157
+ const isMatchingDescription = regex.test(option.description);
158
+ if (!isMatchingTitle && !isMatchingValue && !isMatchingDescription) {
159
+ return;
160
+ }
161
+ this.searchResultOptions.push(option);
162
+ const optionsListItem = document.createElement("div");
163
+ optionsListItem.classList.add('options-list-item');
164
+ optionsListItem.dataset['value'] = option.value;
165
+ optionslist.appendChild(optionsListItem);
166
+ const optionsListItemTitle = document.createElement("div");
167
+ optionsListItemTitle.classList.add('options-list-item-title');
168
+ optionsListItemTitle.innerHTML = DomUtility.boldenText(option.title, searchValue);
169
+ optionsListItem.appendChild(optionsListItemTitle);
170
+ const optionsListItemDescription = document.createElement("div");
171
+ optionsListItemDescription.classList.add('options-list-item-description');
172
+ optionsListItemDescription.innerHTML = DomUtility.boldenText(option.description, searchValue);
173
+ optionsListItem.appendChild(optionsListItemDescription);
174
+ optionsListItem.addEventListener('click', () => {
175
+ this.optionSelectedHandler(option);
176
+ });
177
+ this.componentWrapper.appendChild(optionslist);
178
+ });
179
+ }
180
+
181
+ private closeOptionsList() {
182
+ this.currentFoucsedIndex = -1;
183
+ const optionsList = super.getChildElement('.options-list');
184
+ if (!optionsList) {
185
+ return;
186
+ }
187
+ optionsList.remove();
188
+ }
189
+
190
+ private optionSelectedHandler(option: OptionWithDescription) {
191
+ this.value = option.value;
192
+ this.onChange.emit(new CustomElementEventArgs(this.value, 'change'));
193
+ this.closeOptionsList();
194
+ }
195
+
196
+ private bindNavigation() {
197
+ this.textInputElement.addEventListener('keydown', (event) => {
198
+ if (event.key === 'Escape') {
199
+ this.closeOptionsList();
200
+ }
201
+ if (event.key === 'ArrowDown') {
202
+ if (!this.searchHasResults) {
203
+ return;
204
+ }
205
+ this.currentFoucsedIndex = this.currentFoucsedIndex === this.searchResultOptions.length - 1 ? 0 : this.currentFoucsedIndex + 1;
206
+ this.highlightFocusedSearchResult();
207
+ }
208
+ if (event.key === 'ArrowUp') {
209
+ if (!this.searchHasResults) {
210
+ return;
211
+ }
212
+ this.currentFoucsedIndex = this.currentFoucsedIndex === 0 ? this.searchResultOptions.length - 1 : this.currentFoucsedIndex - 1;
213
+ this.highlightFocusedSearchResult();
214
+ }
215
+ if (event.key === 'Enter') {
216
+ if (this.currentFoucsedIndex !== -1 && this.searchHasResults) {
217
+ this.optionSelectedHandler(this.searchResultOptions[this.currentFoucsedIndex]);
218
+ return;
219
+ }
220
+ }
221
+ });
222
+ }
223
+
224
+ highlightFocusedSearchResult() {
225
+ const optionListItems = super.getChildElements('.options-list-item');
226
+ if (!optionListItems) {
227
+ return;
228
+ }
229
+ optionListItems.forEach((listItem, index) => {
230
+ listItem.classList.remove('active');
231
+ if (index === this.currentFoucsedIndex) {
232
+ listItem.classList.add('active');
233
+ }
234
+ });
235
+ }
236
+
237
+ public change($event): void {
238
+ this.touched = true;
239
+ this.onChange.emit(new CustomElementEventArgs(this.value, 'change'));
240
+ }
241
+
242
+ public validate(): void {
243
+ this.valid;
244
+ this.onValidate.emit(
245
+ new CustomElementEventArgs(this.value, 'validate'),
246
+ );
247
+ }
248
+ }
@@ -1,47 +1,47 @@
1
- export interface Listener<T> {
2
- (event: T): any;
3
- }
4
-
5
- export interface Disposable {
6
- dispose();
7
- }
8
-
9
- export class CustomElementEventArgs {
10
- constructor(public value: any, public eventName: string) {}
11
- }
12
- /** passes through events as they happen. You will not get events from before you start listening */
13
- export class CustomElementEvent<T> {
14
- private listeners: Listener<T>[] = [];
15
- private listenersOncer: Listener<T>[] = [];
16
-
17
- on = (listener: Listener<T>): Disposable => {
18
- this.listeners.push(listener);
19
- return {
20
- dispose: () => this.off(listener),
21
- };
22
- };
23
-
24
- once = (listener: Listener<T>): void => {
25
- this.listenersOncer.push(listener);
26
- };
27
-
28
- off = (listener: Listener<T>) => {
29
- var callbackIndex = this.listeners.indexOf(listener);
30
- if (callbackIndex > -1) this.listeners.splice(callbackIndex, 1);
31
- };
32
-
33
- emit = (event: T) => {
34
- /** Update any general listeners */
35
- this.listeners.forEach(listener => listener(event));
36
-
37
- /** Clear the `once` queue */
38
- if (this.listenersOncer.length > 0) {
39
- this.listenersOncer.forEach(listener => listener(event));
40
- this.listenersOncer = [];
41
- }
42
- };
43
-
44
- pipe = (te: CustomElementEvent<T>): Disposable => {
45
- return this.on(e => te.emit(e));
46
- };
47
- }
1
+ export interface Listener<T> {
2
+ (event: T): any;
3
+ }
4
+
5
+ export interface Disposable {
6
+ dispose();
7
+ }
8
+
9
+ export class CustomElementEventArgs {
10
+ constructor(public value: any, public eventName: string) {}
11
+ }
12
+ /** passes through events as they happen. You will not get events from before you start listening */
13
+ export class CustomElementEvent<T> {
14
+ private listeners: Listener<T>[] = [];
15
+ private listenersOncer: Listener<T>[] = [];
16
+
17
+ on = (listener: Listener<T>): Disposable => {
18
+ this.listeners.push(listener);
19
+ return {
20
+ dispose: () => this.off(listener),
21
+ };
22
+ };
23
+
24
+ once = (listener: Listener<T>): void => {
25
+ this.listenersOncer.push(listener);
26
+ };
27
+
28
+ off = (listener: Listener<T>) => {
29
+ var callbackIndex = this.listeners.indexOf(listener);
30
+ if (callbackIndex > -1) this.listeners.splice(callbackIndex, 1);
31
+ };
32
+
33
+ emit = (event: T) => {
34
+ /** Update any general listeners */
35
+ this.listeners.forEach(listener => listener(event));
36
+
37
+ /** Clear the `once` queue */
38
+ if (this.listenersOncer.length > 0) {
39
+ this.listenersOncer.forEach(listener => listener(event));
40
+ this.listenersOncer = [];
41
+ }
42
+ };
43
+
44
+ pipe = (te: CustomElementEvent<T>): Disposable => {
45
+ return this.on(e => te.emit(e));
46
+ };
47
+ }