@syncfusion/ej2-dropdowns 24.1.44 → 24.1.45-12082

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 (310) hide show
  1. package/CHANGELOG.md +2101 -2083
  2. package/{README.md → ReadMe.md} +217 -217
  3. package/dist/ej2-dropdowns.min.js +1 -10
  4. package/dist/ej2-dropdowns.umd.min.js +1 -10
  5. package/dist/ej2-dropdowns.umd.min.js.map +1 -1
  6. package/dist/es6/ej2-dropdowns.es2015.js +74 -68
  7. package/dist/es6/ej2-dropdowns.es2015.js.map +1 -1
  8. package/dist/es6/ej2-dropdowns.es5.js +216 -210
  9. package/dist/es6/ej2-dropdowns.es5.js.map +1 -1
  10. package/dist/global/ej2-dropdowns.min.js +1 -10
  11. package/dist/global/ej2-dropdowns.min.js.map +1 -1
  12. package/dist/global/index.d.ts +0 -9
  13. package/dist/ts/auto-complete/auto-complete.ts +623 -0
  14. package/dist/ts/combo-box/combo-box.ts +1099 -0
  15. package/dist/ts/common/highlight-search.ts +57 -0
  16. package/dist/ts/common/incremental-search.ts +172 -0
  17. package/dist/ts/common/interface.ts +74 -0
  18. package/dist/ts/common/virtual-scroll.ts +388 -0
  19. package/dist/ts/drop-down-base/drop-down-base.ts +1986 -0
  20. package/dist/ts/drop-down-list/drop-down-list.ts +4261 -0
  21. package/dist/ts/drop-down-tree/drop-down-tree.ts +3747 -0
  22. package/dist/ts/list-box/list-box.ts +2772 -0
  23. package/dist/ts/mention/mention.ts +1875 -0
  24. package/dist/ts/multi-select/checkbox-selection.ts +550 -0
  25. package/dist/ts/multi-select/float-label.ts +176 -0
  26. package/dist/ts/multi-select/interface.ts +70 -0
  27. package/dist/ts/multi-select/multi-select.ts +4882 -0
  28. package/helpers/e2e/autocomplete.js +13 -13
  29. package/helpers/e2e/combobox.js +13 -13
  30. package/helpers/e2e/dropdownlist.js +13 -13
  31. package/helpers/e2e/index.js +3 -3
  32. package/helpers/e2e/listboxHelper.js +13 -13
  33. package/helpers/e2e/multiselect.js +13 -13
  34. package/license +2 -2
  35. package/package.json +80 -80
  36. package/src/auto-complete/auto-complete-model.d.ts +188 -188
  37. package/src/auto-complete/auto-complete.d.ts +12 -12
  38. package/src/auto-complete/auto-complete.js +21 -21
  39. package/src/combo-box/combo-box-model.d.ts +224 -224
  40. package/src/combo-box/combo-box.d.ts +27 -27
  41. package/src/combo-box/combo-box.js +29 -29
  42. package/src/common/virtual-scroll.js +46 -46
  43. package/src/drop-down-base/drop-down-base-model.d.ts +200 -200
  44. package/src/drop-down-base/drop-down-base.d.ts +16 -16
  45. package/src/drop-down-base/drop-down-base.js +20 -20
  46. package/src/drop-down-list/drop-down-list-model.d.ts +202 -202
  47. package/src/drop-down-list/drop-down-list.d.ts +5 -4
  48. package/src/drop-down-list/drop-down-list.js +29 -22
  49. package/src/drop-down-tree/drop-down-tree-model.d.ts +468 -468
  50. package/src/drop-down-tree/drop-down-tree.d.ts +0 -1
  51. package/src/drop-down-tree/drop-down-tree.js +19 -27
  52. package/src/list-box/list-box-model.d.ts +193 -193
  53. package/src/list-box/list-box.d.ts +2 -2
  54. package/src/list-box/list-box.js +19 -19
  55. package/src/mention/mention-model.d.ts +261 -261
  56. package/src/mention/mention.js +27 -20
  57. package/src/multi-select/multi-select-model.d.ts +512 -512
  58. package/src/multi-select/multi-select.js +20 -20
  59. package/styles/auto-complete/_all.scss +1 -1
  60. package/styles/auto-complete/_bds-definition.scss +2 -0
  61. package/styles/auto-complete/_bootstrap-dark-definition.scss +3 -3
  62. package/styles/auto-complete/_bootstrap-definition.scss +2 -2
  63. package/styles/auto-complete/_bootstrap4-definition.scss +11 -11
  64. package/styles/auto-complete/_bootstrap5-definition.scss +2 -2
  65. package/styles/auto-complete/_fabric-dark-definition.scss +2 -2
  66. package/styles/auto-complete/_fabric-definition.scss +2 -2
  67. package/styles/auto-complete/_fluent-definition.scss +2 -2
  68. package/styles/auto-complete/_fusionnew-definition.scss +2 -2
  69. package/styles/auto-complete/_highcontrast-definition.scss +2 -2
  70. package/styles/auto-complete/_highcontrast-light-definition.scss +2 -2
  71. package/styles/auto-complete/_material-dark-definition.scss +2 -2
  72. package/styles/auto-complete/_material-definition.scss +2 -2
  73. package/styles/auto-complete/_material3-definition.scss +2 -2
  74. package/styles/auto-complete/_tailwind-definition.scss +2 -2
  75. package/styles/auto-complete/material3-dark.scss +1 -1
  76. package/styles/auto-complete/material3.scss +1 -1
  77. package/styles/bootstrap-dark.css +5 -0
  78. package/styles/bootstrap.css +5 -0
  79. package/styles/bootstrap4.css +5 -0
  80. package/styles/bootstrap5-dark.css +5 -0
  81. package/styles/bootstrap5.css +5 -0
  82. package/styles/combo-box/_all.scss +1 -1
  83. package/styles/combo-box/_bds-definition.scss +2 -0
  84. package/styles/combo-box/_bootstrap-dark-definition.scss +2 -2
  85. package/styles/combo-box/_bootstrap-definition.scss +2 -2
  86. package/styles/combo-box/_bootstrap4-definition.scss +11 -11
  87. package/styles/combo-box/_bootstrap5-definition.scss +2 -2
  88. package/styles/combo-box/_fabric-dark-definition.scss +2 -2
  89. package/styles/combo-box/_fabric-definition.scss +2 -2
  90. package/styles/combo-box/_fluent-definition.scss +2 -2
  91. package/styles/combo-box/_fusionnew-definition.scss +2 -2
  92. package/styles/combo-box/_highcontrast-definition.scss +2 -2
  93. package/styles/combo-box/_highcontrast-light-definition.scss +3 -3
  94. package/styles/combo-box/_material-dark-definition.scss +2 -2
  95. package/styles/combo-box/_material-definition.scss +2 -2
  96. package/styles/combo-box/_material3-definition.scss +2 -2
  97. package/styles/combo-box/_tailwind-definition.scss +2 -2
  98. package/styles/combo-box/material3-dark.scss +1 -1
  99. package/styles/combo-box/material3.scss +1 -1
  100. package/styles/drop-down-base/_all.scss +2 -2
  101. package/styles/drop-down-base/_bds-definition.scss +129 -0
  102. package/styles/drop-down-base/_bootstrap-dark-definition.scss +83 -83
  103. package/styles/drop-down-base/_bootstrap-definition.scss +83 -83
  104. package/styles/drop-down-base/_bootstrap4-definition.scss +90 -90
  105. package/styles/drop-down-base/_bootstrap5-definition.scss +117 -117
  106. package/styles/drop-down-base/_definition.scss +23 -23
  107. package/styles/drop-down-base/_fabric-dark-definition.scss +86 -86
  108. package/styles/drop-down-base/_fabric-definition.scss +84 -84
  109. package/styles/drop-down-base/_fluent-definition.scss +121 -121
  110. package/styles/drop-down-base/_fusionnew-definition.scss +117 -117
  111. package/styles/drop-down-base/_highcontrast-definition.scss +105 -105
  112. package/styles/drop-down-base/_highcontrast-light-definition.scss +105 -105
  113. package/styles/drop-down-base/_layout.scss +192 -192
  114. package/styles/drop-down-base/_material-dark-definition.scss +86 -86
  115. package/styles/drop-down-base/_material-definition.scss +85 -85
  116. package/styles/drop-down-base/_material3-definition.scss +87 -87
  117. package/styles/drop-down-base/_tailwind-definition.scss +129 -129
  118. package/styles/drop-down-base/_theme.scss +391 -391
  119. package/styles/drop-down-base/material3-dark.scss +1 -1
  120. package/styles/drop-down-base/material3.scss +1 -1
  121. package/styles/drop-down-list/_all.scss +3 -3
  122. package/styles/drop-down-list/_bds-definition.scss +134 -0
  123. package/styles/drop-down-list/_bootstrap-dark-definition.scss +157 -157
  124. package/styles/drop-down-list/_bootstrap-definition.scss +156 -156
  125. package/styles/drop-down-list/_bootstrap4-definition.scss +202 -202
  126. package/styles/drop-down-list/_bootstrap5-definition.scss +201 -201
  127. package/styles/drop-down-list/_fabric-dark-definition.scss +128 -128
  128. package/styles/drop-down-list/_fabric-definition.scss +124 -124
  129. package/styles/drop-down-list/_fluent-definition.scss +185 -185
  130. package/styles/drop-down-list/_fusionnew-definition.scss +201 -201
  131. package/styles/drop-down-list/_highcontrast-definition.scss +142 -142
  132. package/styles/drop-down-list/_highcontrast-light-definition.scss +144 -144
  133. package/styles/drop-down-list/_layout.scss +315 -310
  134. package/styles/drop-down-list/_material-dark-definition.scss +143 -143
  135. package/styles/drop-down-list/_material-definition.scss +167 -167
  136. package/styles/drop-down-list/_material3-definition.scss +180 -180
  137. package/styles/drop-down-list/_tailwind-definition.scss +134 -134
  138. package/styles/drop-down-list/_theme.scss +10 -10
  139. package/styles/drop-down-list/bootstrap-dark.css +5 -0
  140. package/styles/drop-down-list/bootstrap.css +5 -0
  141. package/styles/drop-down-list/bootstrap4.css +5 -0
  142. package/styles/drop-down-list/bootstrap5-dark.css +5 -0
  143. package/styles/drop-down-list/bootstrap5.css +5 -0
  144. package/styles/drop-down-list/fabric-dark.css +5 -0
  145. package/styles/drop-down-list/fabric.css +5 -0
  146. package/styles/drop-down-list/fluent-dark.css +5 -0
  147. package/styles/drop-down-list/fluent.css +5 -0
  148. package/styles/drop-down-list/highcontrast-light.css +5 -0
  149. package/styles/drop-down-list/highcontrast.css +5 -0
  150. package/styles/drop-down-list/icons/_bootstrap-dark.scss +14 -14
  151. package/styles/drop-down-list/icons/_bootstrap.scss +14 -14
  152. package/styles/drop-down-list/icons/_bootstrap4.scss +14 -14
  153. package/styles/drop-down-list/icons/_bootstrap5.scss +14 -14
  154. package/styles/drop-down-list/icons/_fabric-dark.scss +14 -14
  155. package/styles/drop-down-list/icons/_fabric.scss +14 -14
  156. package/styles/drop-down-list/icons/_fluent.scss +14 -14
  157. package/styles/drop-down-list/icons/_fusionnew.scss +14 -14
  158. package/styles/drop-down-list/icons/_highcontrast-light.scss +14 -14
  159. package/styles/drop-down-list/icons/_highcontrast.scss +14 -14
  160. package/styles/drop-down-list/icons/_material-dark.scss +14 -14
  161. package/styles/drop-down-list/icons/_material.scss +14 -14
  162. package/styles/drop-down-list/icons/_material3.scss +14 -14
  163. package/styles/drop-down-list/icons/_tailwind.scss +14 -14
  164. package/styles/drop-down-list/material-dark.css +5 -0
  165. package/styles/drop-down-list/material.css +5 -0
  166. package/styles/drop-down-list/material3-dark.css +5 -0
  167. package/styles/drop-down-list/material3-dark.scss +1 -1
  168. package/styles/drop-down-list/material3.css +5 -0
  169. package/styles/drop-down-list/material3.scss +1 -1
  170. package/styles/drop-down-list/tailwind-dark.css +5 -0
  171. package/styles/drop-down-list/tailwind.css +5 -0
  172. package/styles/drop-down-tree/_all.scss +2 -2
  173. package/styles/drop-down-tree/_bds-definition.scss +61 -0
  174. package/styles/drop-down-tree/_bootstrap-dark-definition.scss +71 -71
  175. package/styles/drop-down-tree/_bootstrap-definition.scss +70 -70
  176. package/styles/drop-down-tree/_bootstrap4-definition.scss +71 -71
  177. package/styles/drop-down-tree/_bootstrap5-definition.scss +59 -59
  178. package/styles/drop-down-tree/_fabric-dark-definition.scss +71 -71
  179. package/styles/drop-down-tree/_fabric-definition.scss +71 -71
  180. package/styles/drop-down-tree/_fluent-definition.scss +65 -65
  181. package/styles/drop-down-tree/_fusionnew-definition.scss +59 -59
  182. package/styles/drop-down-tree/_highcontrast-definition.scss +71 -71
  183. package/styles/drop-down-tree/_highcontrast-light-definition.scss +71 -71
  184. package/styles/drop-down-tree/_layout.scss +1423 -1423
  185. package/styles/drop-down-tree/_material-dark-definition.scss +72 -72
  186. package/styles/drop-down-tree/_material-definition.scss +72 -72
  187. package/styles/drop-down-tree/_material3-definition.scss +76 -76
  188. package/styles/drop-down-tree/_tailwind-definition.scss +61 -61
  189. package/styles/drop-down-tree/_theme.scss +132 -132
  190. package/styles/drop-down-tree/icons/_bootstrap-dark.scss +11 -11
  191. package/styles/drop-down-tree/icons/_bootstrap.scss +11 -11
  192. package/styles/drop-down-tree/icons/_bootstrap4.scss +11 -11
  193. package/styles/drop-down-tree/icons/_bootstrap5.scss +11 -11
  194. package/styles/drop-down-tree/icons/_fabric-dark.scss +11 -11
  195. package/styles/drop-down-tree/icons/_fabric.scss +11 -11
  196. package/styles/drop-down-tree/icons/_fluent.scss +11 -11
  197. package/styles/drop-down-tree/icons/_fusionnew.scss +11 -11
  198. package/styles/drop-down-tree/icons/_highcontrast-light.scss +11 -11
  199. package/styles/drop-down-tree/icons/_highcontrast.scss +11 -11
  200. package/styles/drop-down-tree/icons/_material-dark.scss +11 -11
  201. package/styles/drop-down-tree/icons/_material.scss +11 -11
  202. package/styles/drop-down-tree/icons/_material3.scss +11 -11
  203. package/styles/drop-down-tree/icons/_tailwind-dark.scss +11 -11
  204. package/styles/drop-down-tree/icons/_tailwind.scss +11 -11
  205. package/styles/drop-down-tree/material3-dark.scss +1 -1
  206. package/styles/drop-down-tree/material3.scss +1 -1
  207. package/styles/fabric-dark.css +5 -0
  208. package/styles/fabric.css +5 -0
  209. package/styles/fluent-dark.css +5 -0
  210. package/styles/fluent.css +5 -0
  211. package/styles/highcontrast-light.css +5 -0
  212. package/styles/highcontrast.css +5 -0
  213. package/styles/list-box/_all.scss +2 -2
  214. package/styles/list-box/_bds-definition.scss +119 -0
  215. package/styles/list-box/_bootstrap-dark-definition.scss +126 -126
  216. package/styles/list-box/_bootstrap-definition.scss +119 -119
  217. package/styles/list-box/_bootstrap4-definition.scss +124 -124
  218. package/styles/list-box/_bootstrap5-definition.scss +120 -120
  219. package/styles/list-box/_fabric-dark-definition.scss +126 -126
  220. package/styles/list-box/_fabric-definition.scss +119 -119
  221. package/styles/list-box/_fluent-definition.scss +120 -120
  222. package/styles/list-box/_fusionnew-definition.scss +111 -111
  223. package/styles/list-box/_highcontrast-definition.scss +119 -119
  224. package/styles/list-box/_highcontrast-light-definition.scss +126 -126
  225. package/styles/list-box/_layout.scss +542 -542
  226. package/styles/list-box/_material-dark-definition.scss +126 -126
  227. package/styles/list-box/_material-definition.scss +119 -119
  228. package/styles/list-box/_material3-definition.scss +119 -119
  229. package/styles/list-box/_tailwind-definition.scss +119 -119
  230. package/styles/list-box/_theme.scss +382 -382
  231. package/styles/list-box/icons/_bootstrap-dark.scss +25 -25
  232. package/styles/list-box/icons/_bootstrap.scss +25 -25
  233. package/styles/list-box/icons/_bootstrap4.scss +25 -25
  234. package/styles/list-box/icons/_bootstrap5.scss +25 -25
  235. package/styles/list-box/icons/_fabric-dark.scss +25 -25
  236. package/styles/list-box/icons/_fabric.scss +25 -25
  237. package/styles/list-box/icons/_fluent.scss +25 -25
  238. package/styles/list-box/icons/_fusionnew.scss +25 -25
  239. package/styles/list-box/icons/_highcontrast-light.scss +25 -25
  240. package/styles/list-box/icons/_highcontrast.scss +25 -25
  241. package/styles/list-box/icons/_material-dark.scss +25 -25
  242. package/styles/list-box/icons/_material.scss +25 -25
  243. package/styles/list-box/icons/_material3.scss +25 -25
  244. package/styles/list-box/icons/_tailwind-dark.scss +25 -25
  245. package/styles/list-box/icons/_tailwind.scss +25 -25
  246. package/styles/list-box/material3-dark.scss +1 -1
  247. package/styles/list-box/material3.scss +1 -1
  248. package/styles/material-dark.css +5 -0
  249. package/styles/material.css +5 -0
  250. package/styles/material3-dark.css +5 -0
  251. package/styles/material3-dark.scss +1 -1
  252. package/styles/material3.css +5 -0
  253. package/styles/material3.scss +1 -1
  254. package/styles/mention/_all.scss +1 -1
  255. package/styles/mention/_bds-definition.scss +1 -0
  256. package/styles/mention/_bootstrap-dark-definition.scss +3 -3
  257. package/styles/mention/_bootstrap-definition.scss +3 -3
  258. package/styles/mention/_bootstrap4-definition.scss +3 -3
  259. package/styles/mention/_bootstrap5-definition.scss +1 -1
  260. package/styles/mention/_fabric-dark-definition.scss +2 -2
  261. package/styles/mention/_fabric-definition.scss +3 -3
  262. package/styles/mention/_fluent-definition.scss +1 -1
  263. package/styles/mention/_fusionnew-definition.scss +1 -1
  264. package/styles/mention/_highcontrast-definition.scss +3 -3
  265. package/styles/mention/_highcontrast-light-definition.scss +3 -3
  266. package/styles/mention/_layout.scss +6 -6
  267. package/styles/mention/_material-dark-definition.scss +3 -3
  268. package/styles/mention/_material-definition.scss +3 -3
  269. package/styles/mention/_material3-definition.scss +1 -1
  270. package/styles/mention/_tailwind-definition.scss +1 -1
  271. package/styles/mention/material3-dark.scss +1 -1
  272. package/styles/mention/material3.scss +1 -1
  273. package/styles/multi-select/_all.scss +2 -2
  274. package/styles/multi-select/_bds-definition.scss +235 -0
  275. package/styles/multi-select/_bootstrap-dark-definition.scss +203 -203
  276. package/styles/multi-select/_bootstrap-definition.scss +192 -192
  277. package/styles/multi-select/_bootstrap4-definition.scss +278 -278
  278. package/styles/multi-select/_bootstrap5-definition.scss +230 -230
  279. package/styles/multi-select/_fabric-dark-definition.scss +192 -192
  280. package/styles/multi-select/_fabric-definition.scss +183 -183
  281. package/styles/multi-select/_fluent-definition.scss +241 -241
  282. package/styles/multi-select/_fusionnew-definition.scss +227 -227
  283. package/styles/multi-select/_highcontrast-definition.scss +303 -303
  284. package/styles/multi-select/_highcontrast-light-definition.scss +297 -297
  285. package/styles/multi-select/_layout.scss +2199 -2199
  286. package/styles/multi-select/_material-dark-definition.scss +230 -230
  287. package/styles/multi-select/_material-definition.scss +223 -223
  288. package/styles/multi-select/_material3-definition.scss +246 -246
  289. package/styles/multi-select/_tailwind-definition.scss +235 -235
  290. package/styles/multi-select/_theme.scss +586 -586
  291. package/styles/multi-select/icons/_bootstrap-dark.scss +26 -26
  292. package/styles/multi-select/icons/_bootstrap.scss +26 -26
  293. package/styles/multi-select/icons/_bootstrap4.scss +37 -37
  294. package/styles/multi-select/icons/_bootstrap5.scss +26 -26
  295. package/styles/multi-select/icons/_fabric-dark.scss +26 -26
  296. package/styles/multi-select/icons/_fabric.scss +26 -26
  297. package/styles/multi-select/icons/_fluent.scss +55 -55
  298. package/styles/multi-select/icons/_fusionnew.scss +26 -26
  299. package/styles/multi-select/icons/_highcontrast-light.scss +26 -26
  300. package/styles/multi-select/icons/_highcontrast.scss +26 -26
  301. package/styles/multi-select/icons/_material-dark.scss +693 -693
  302. package/styles/multi-select/icons/_material.scss +693 -693
  303. package/styles/multi-select/icons/_material3.scss +692 -692
  304. package/styles/multi-select/icons/_tailwind.scss +26 -26
  305. package/styles/multi-select/material3-dark.scss +1 -1
  306. package/styles/multi-select/material3.scss +1 -1
  307. package/styles/tailwind-dark.css +5 -0
  308. package/styles/tailwind.css +5 -0
  309. package/.eslintrc.json +0 -260
  310. package/tslint.json +0 -111
@@ -0,0 +1,1875 @@
1
+ import { KeyboardEvents, compile, Property, EventHandler, Animation, AnimationModel, KeyboardEventArgs, formatUnit, append, attributes } from '@syncfusion/ej2-base';
2
+ import { isNullOrUndefined, detach, Event, EmitType, Complex, addClass, removeClass, closest, isUndefined, getValue, NotifyPropertyChanges, Browser } from '@syncfusion/ej2-base';
3
+ import { FieldSettingsModel } from '../drop-down-base/drop-down-base-model';
4
+ import { FieldSettings, FilteringEventArgs, FilterType } from '../drop-down-base/drop-down-base';
5
+ import { DropDownBase, PopupEventArgs, SelectEventArgs, BeforeOpenEventArgs, dropDownBaseClasses } from '../drop-down-base/drop-down-base';
6
+ import { DataManager, Query } from '@syncfusion/ej2-data';
7
+ import { MentionModel } from '../mention/mention-model';
8
+ import { SortOrder } from '@syncfusion/ej2-lists';
9
+ import { Popup, isCollide, createSpinner, showSpinner, hideSpinner, getZindexPartial } from '@syncfusion/ej2-popups';
10
+ import { highlightSearch, revertHighlightSearch } from '../common/highlight-search';
11
+
12
+ export interface MentionChangeEventArgs extends SelectEventArgs {
13
+ /**
14
+ * Specifies the selected value.
15
+ *
16
+ * @isGenericType true
17
+ */
18
+ value: number | string | boolean
19
+ /**
20
+ * Specifies the element of previous selected list item.
21
+ */
22
+ previousItem: HTMLLIElement
23
+ /**
24
+ * Specifies the previously selected item as a JSON Object from the data source.
25
+ *
26
+ */
27
+ previousItemData: FieldSettingsModel
28
+ /**
29
+ * Specifies the component root element.
30
+ */
31
+ element: HTMLElement
32
+ }
33
+
34
+ /**
35
+ * The Mention component is used to list someone or something based on user input in textarea, input,
36
+ * or any other editable element from which the user can select.
37
+ */
38
+ @NotifyPropertyChanges
39
+ export class Mention extends DropDownBase {
40
+ private initRemoteRender: boolean;
41
+ private inputElement: HTMLInputElement | HTMLTextAreaElement | HTMLElement;
42
+ private popupObj: Popup;
43
+ private isPopupOpen: boolean;
44
+ private isSelected: boolean;
45
+ private selectedLI: HTMLLIElement;
46
+ private previousSelectedLI: HTMLElement;
47
+ private previousItemData: { [key: string]: Object } | string | number | boolean;
48
+ private activeIndex: number;
49
+ private keyConfigure: { [key: string]: string };
50
+ private isFiltered: boolean;
51
+ private beforePopupOpen: boolean;
52
+ private listHeight: string;
53
+ private isListResetted: boolean;
54
+ private range: Range;
55
+ private displayTempElement: HTMLElement;
56
+ private isCollided: boolean;
57
+ private collision: string[];
58
+ private spinnerElement: HTMLElement;
59
+ private spinnerTemplateElement: HTMLElement;
60
+ private lineBreak: boolean;
61
+ private selectedElementID : string;
62
+ private isSelectCancel: boolean;
63
+ private isTyped: boolean;
64
+ private didPopupOpenByTypingInitialChar: boolean;
65
+ private isUpDownKey: boolean;
66
+
67
+ // Mention Options
68
+
69
+ /**
70
+ * Defines class/multiple classes separated by a space for the mention component.
71
+ *
72
+ * @default null
73
+ */
74
+ @Property(null)
75
+ public cssClass: string;
76
+
77
+ /**
78
+ * Specifies the symbol or single character which triggers the search action in the mention component.
79
+ *
80
+ * @default '@'
81
+ * @aspType char
82
+ */
83
+ @Property('@')
84
+ public mentionChar: string;
85
+
86
+ /**
87
+ * Specifies whether to show the configured mentionChar with the text.
88
+ *
89
+ * @default false
90
+ */
91
+ @Property(false)
92
+ public showMentionChar: boolean;
93
+
94
+ /**
95
+ * Defines whether to allow the space in the middle of mention while searching.
96
+ * When disabled, the space ends the mention component search.
97
+ *
98
+ * @default false
99
+ */
100
+ @Property(false)
101
+ public allowSpaces: boolean;
102
+
103
+ /**
104
+ * Specifies the custom suffix to append along with the mention component selected item while inserting.
105
+ * You can append space or new line character as suffix.
106
+ *
107
+ * @default null
108
+ */
109
+ @Property(null)
110
+ public suffixText: string;
111
+
112
+ /**
113
+ * Specifies the number of items in the suggestion list.
114
+ *
115
+ * @default 25
116
+ * @aspType int
117
+ */
118
+ @Property(25)
119
+ public suggestionCount: number;
120
+
121
+ /**
122
+ * Specifies the minimum length of user input to initiate the search action.
123
+ * The default value is zero, where suggestion the list opened as soon as the user inputs the mention character.
124
+ *
125
+ * @default 0
126
+ * @aspType int
127
+ */
128
+ @Property(0)
129
+ public minLength: number;
130
+
131
+ /**
132
+ * Specifies the order to sort the data source. The possible sort orders are,
133
+ * * `None` - The data source is not sorted.
134
+ * * `Ascending` - The data source is sorted in ascending order.
135
+ * * `Descending` - The data source is sorted in descending order.
136
+ *
137
+ * @default 'None'
138
+ */
139
+ @Property('None')
140
+ public sortOrder: SortOrder;
141
+
142
+ /**
143
+ * Specifies whether the searches are case sensitive to find suggestions.
144
+ *
145
+ * @default true
146
+ */
147
+ @Property(true)
148
+ public ignoreCase: boolean;
149
+
150
+ /**
151
+ * Specifies whether to highlight the searched characters on suggestion list items.
152
+ *
153
+ * @default false
154
+ */
155
+ @Property(false)
156
+ public highlight: boolean;
157
+
158
+ /**
159
+ * Overrides the global culture and localization value for this component. Default global culture is ‘en-US’.
160
+ *
161
+ * @default 'en-US'
162
+ */
163
+ @Property()
164
+ public locale: string;
165
+
166
+ /**
167
+ * Specifies the width of the popup in pixels/number/percentage. The number value is considered as pixels.
168
+ *
169
+ * @default 'auto'
170
+ * @aspType string
171
+ */
172
+ @Property('auto')
173
+ public popupWidth: string | number;
174
+
175
+ /**
176
+ * Specifies the height of the popup in pixels/number/percentage. The number value is considered as pixels.
177
+ *
178
+ * @default '300px'
179
+ * @aspType string
180
+ */
181
+ @Property('300px')
182
+ public popupHeight: string | number;
183
+
184
+ /**
185
+ * Specifies the template for the selected value from the suggestion list.
186
+ *
187
+ * @default null
188
+ * @aspType string
189
+ */
190
+ @Property(null)
191
+ public displayTemplate: string | Function;
192
+
193
+ /**
194
+ * Specifies the template for the suggestion list.
195
+ *
196
+ * @default null
197
+ */
198
+ @Property(null)
199
+ public itemTemplate: string;
200
+
201
+ /**
202
+ * Specifies the template for no matched item which is displayed when there are no items to display in the suggestion list.
203
+ *
204
+ * @default 'No records found'
205
+ */
206
+ @Property('No records found')
207
+ public noRecordsTemplate: string;
208
+
209
+ /**
210
+ * Specifies the template for showing until data is loaded in the popup.
211
+ *
212
+ * @default null
213
+ * @aspType string
214
+ */
215
+ @Property(null)
216
+ public spinnerTemplate: string | Function;
217
+
218
+ /**
219
+ * Specifies the target selector where the mention component needs to be displayed.
220
+ * The mention component listens to the target's user input and displays suggestions as soon as the user inputs the mention character.
221
+ *
222
+ */
223
+ @Property()
224
+ public target: HTMLElement | string;
225
+
226
+ /**
227
+ * Accepts the list items either through local or remote service and binds it to the component.
228
+ * It can be an array of JSON Objects or an instance of `DataManager`.
229
+ *
230
+ * @default []
231
+ */
232
+ @Property([])
233
+ public dataSource: { [key: string]: Object }[] | DataManager | string[] | number[] | boolean[];
234
+
235
+ /**
236
+ * Specifies the external query, which can be customized and filtered against the data source.
237
+ *
238
+ * @default null
239
+ */
240
+ @Property(null)
241
+ public query: Query;
242
+
243
+ /**
244
+ * Determines on which filter type, the component needs to be considered on search action.
245
+ * and its supported data types are
246
+ *
247
+ * <table>
248
+ * <tr>
249
+ * <td colSpan=1 rowSpan=1>
250
+ * FilterType<br/></td><td colSpan=1 rowSpan=1>
251
+ * Description<br/></td><td colSpan=1 rowSpan=1>
252
+ * Supported Types<br/></td></tr>
253
+ * <tr>
254
+ * <td colSpan=1 rowSpan=1>
255
+ * StartsWith<br/></td><td colSpan=1 rowSpan=1>
256
+ * Checks whether a value begins with the specified value.<br/></td><td colSpan=1 rowSpan=1>
257
+ * String<br/></td></tr>
258
+ * <tr>
259
+ * <td colSpan=1 rowSpan=1>
260
+ * EndsWith<br/></td><td colSpan=1 rowSpan=1>
261
+ * Checks whether a value ends with a specified value.<br/><br/></td><td colSpan=1 rowSpan=1>
262
+ * <br/>String<br/></td></tr>
263
+ * <tr>
264
+ * <td colSpan=1 rowSpan=1>
265
+ * Contains<br/></td><td colSpan=1 rowSpan=1>
266
+ * Checks whether a value contains with a specified value.<br/><br/></td><td colSpan=1 rowSpan=1>
267
+ * <br/>String<br/></td></tr>
268
+ * </table>
269
+ *
270
+ * The default value set to `Contains`, all the suggestion items which contain typed characters to listed in the suggestion popup.
271
+ *
272
+ * @default 'Contains'
273
+ */
274
+ @Property('Contains')
275
+ public filterType: FilterType;
276
+
277
+ /**
278
+ * Defines the fields of the Mention to map with the data source and binds the data to the component.
279
+ * * text - Specifies the text that maps the text filed from the data source for each list item.
280
+ * * value - Specifies the value that maps the value filed from the data source for each list item.
281
+ * * iconCss - Specifies the iconCss that map the icon class filed from the data source for each list item.
282
+ * * groupBy - Specifies the groupBy that groups the list items with its related items by mapping groupBy field.
283
+ *
284
+ * @default
285
+ * {
286
+ * text: null, value: null, iconCss: null, groupBy: null
287
+ * }
288
+ */
289
+ @Complex<FieldSettingsModel>({ text: null, value: null, iconCss: null, groupBy: null }, FieldSettings)
290
+ public fields: FieldSettingsModel;
291
+
292
+ /**
293
+ * Triggers before fetching data from the remote server.
294
+ *
295
+ * @event actionBegin
296
+ */
297
+ @Event()
298
+ public actionBegin: EmitType<Object>;
299
+
300
+ /**
301
+ * Triggers after data is fetched successfully from the remote server.
302
+ *
303
+ * @event actionComplete
304
+ */
305
+ @Event()
306
+ public actionComplete: EmitType<Object>;
307
+
308
+ /**
309
+ * Triggers when the data fetch request from the remote server fails.
310
+ *
311
+ * @event actionFailure
312
+ */
313
+ @Event()
314
+ public actionFailure: EmitType<Object>;
315
+
316
+ /**
317
+ * Triggers when an item in a popup is selected and updated in an editor.
318
+ *
319
+ * @event change
320
+ */
321
+ @Event()
322
+ public change: EmitType<MentionChangeEventArgs>;
323
+
324
+ /**
325
+ * Triggers before the popup is opened.
326
+ *
327
+ * @event beforeOpen
328
+ */
329
+ @Event()
330
+ public beforeOpen: EmitType<PopupEventArgs>;
331
+
332
+ /**
333
+ * Triggers after the popup opens.
334
+ *
335
+ * @event opened
336
+ */
337
+ @Event()
338
+ public opened: EmitType<PopupEventArgs>;
339
+
340
+ /**
341
+ * Triggers after the popup is closed.
342
+ *
343
+ * @event closed
344
+ */
345
+ @Event()
346
+ public closed: EmitType<PopupEventArgs>;
347
+
348
+ /**
349
+ * Triggers when an item in the popup is selected by the user either with the mouse/tap or with keyboard navigation.
350
+ *
351
+ * @event select
352
+ */
353
+ @Event()
354
+ public select: EmitType<SelectEventArgs>;
355
+
356
+ /**
357
+ * Triggers on typing a character in the component.
358
+ *
359
+ * @event filtering
360
+ */
361
+ @Event()
362
+ public filtering: EmitType<FilteringEventArgs>;
363
+
364
+ /**
365
+ * Triggers when the component is created.
366
+ *
367
+ * @event created
368
+ */
369
+ @Event()
370
+ public created: EmitType<Object>;
371
+
372
+ /**
373
+      * Triggers when the component is destroyed.
374
+ *
375
+      * @event destroyed
376
+      */
377
+ @Event()
378
+ public destroyed: EmitType<Object>;
379
+
380
+ /**
381
+ * * Constructor for creating the widget
382
+ *
383
+ * @param {MentionModel} options - Specifies the MentionComponent model.
384
+ * @param {string | HTMLElement} element - Specifies the element to render as component.
385
+ * @private
386
+ */
387
+ public constructor(options?: MentionModel, element?: string | HTMLElement) {
388
+ super(options, element);
389
+ }
390
+
391
+ /**
392
+ * When property value changes happened, then onPropertyChanged method will execute the respective changes in this component.
393
+ *
394
+ * @param {MentionModel} newProp - Returns the dynamic property value of the component.
395
+ * @param {MentionModel} oldProp - Returns the previous property value of the component.
396
+ * @private
397
+ * @returns {void}
398
+ */
399
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
400
+ public onPropertyChanged(newProp: MentionModel, oldProp: MentionModel): void {
401
+ for (const prop of Object.keys(newProp)) {
402
+ switch (prop) {
403
+ case 'minLength':
404
+ this.minLength = newProp.minLength;
405
+ break;
406
+ case 'suffixText':
407
+ this.suffixText = newProp.suffixText;
408
+ break;
409
+ case 'allowSpaces':
410
+ this.allowSpaces = newProp.allowSpaces;
411
+ break;
412
+ case 'mentionChar':
413
+ this.mentionChar = newProp.mentionChar;
414
+ break;
415
+ case 'showMentionChar':
416
+ this.showMentionChar = newProp.showMentionChar;
417
+ break;
418
+ case 'cssClass': this.updateCssClass(newProp.cssClass, oldProp.cssClass); break;
419
+ }
420
+ }
421
+ }
422
+
423
+ private updateCssClass(newClass: string, oldClass: string): void {
424
+ if (!isNullOrUndefined(oldClass)) {
425
+ oldClass = (oldClass.replace(/\s+/g, ' ')).trim();
426
+ }
427
+ if (!isNullOrUndefined(newClass)) {
428
+ newClass = (newClass.replace(/\s+/g, ' ')).trim();
429
+ }
430
+ this.setCssClass(newClass, [this.inputElement], oldClass);
431
+ if (this.popupObj) {
432
+ this.setCssClass(newClass, [this.popupObj.element], oldClass);
433
+ }
434
+ }
435
+
436
+ private setCssClass(cssClass: string, elements: Element[] | NodeList, oldClass?: string): void {
437
+ if (!isNullOrUndefined(oldClass) && oldClass !== '') {
438
+ removeClass(elements, oldClass.split(' '));
439
+ }
440
+ if (!isNullOrUndefined(cssClass) && cssClass !== '') {
441
+ addClass(elements, cssClass.split(' '));
442
+ }
443
+ }
444
+
445
+ private initializeData(): void {
446
+ this.isSelected = false;
447
+ this.isFiltered = false;
448
+ this.beforePopupOpen = false;
449
+ this.initRemoteRender = false;
450
+ this.isListResetted = false;
451
+ this.isPopupOpen = false;
452
+ this.isCollided = false;
453
+ this.lineBreak = false;
454
+ this.keyConfigure = {
455
+ tab: 'tab',
456
+ enter: '13',
457
+ escape: '27',
458
+ end: '35',
459
+ home: '36',
460
+ down: '40',
461
+ up: '38',
462
+ pageUp: '33',
463
+ pageDown: '34',
464
+ open: 'alt+40',
465
+ close: 'shift+tab',
466
+ hide: 'alt+38',
467
+ space: '32'
468
+ };
469
+ }
470
+
471
+ /**
472
+ * Execute before render the list items
473
+ *
474
+ * @private
475
+ * @returns {void}
476
+ */
477
+ protected preRender(): void {
478
+ this.initializeData();
479
+ super.preRender();
480
+ }
481
+
482
+ /**
483
+ * To Initialize the control rendering
484
+ *
485
+ * @private
486
+ * @returns {void}
487
+ */
488
+ public render(): void {
489
+ const isSelector = typeof this.target === 'string';
490
+ this.inputElement = !isNullOrUndefined(this.target) ?
491
+ this.checkAndUpdateInternalComponent(isSelector
492
+ ? <HTMLElement>document.querySelector(<string>this.target)
493
+ : <HTMLElement>this.target) : this.element;
494
+ if (this.isContentEditable(this.inputElement)) {
495
+ this.inputElement.setAttribute('contenteditable', 'true');
496
+ addClass([this.inputElement], ['e-mention']);
497
+ if (isNullOrUndefined(this.target)) {
498
+ addClass([this.inputElement], ['e-editable-element']);
499
+ }
500
+ }
501
+ this.inputElement.setAttribute('role', 'textbox');
502
+ this.inputElement.setAttribute('aria-label', 'mention');
503
+ this.queryString = this.elementValue();
504
+ this.wireEvent();
505
+ }
506
+
507
+ private wireEvent(): void {
508
+ EventHandler.add(this.inputElement, 'keyup', this.onKeyUp, this);
509
+ this.bindCommonEvent();
510
+ }
511
+
512
+ private unWireEvent(): void {
513
+ EventHandler.remove(this.inputElement, 'keyup', this.onKeyUp);
514
+ this.unBindCommonEvent();
515
+ }
516
+
517
+ private bindCommonEvent(): void {
518
+ if (!Browser.isDevice) {
519
+ this.keyboardModule = new KeyboardEvents(
520
+ this.inputElement, {
521
+ keyAction: this.keyActionHandler.bind(this), keyConfigs: this.keyConfigure, eventName: 'keydown'
522
+ });
523
+ }
524
+ }
525
+
526
+ /**
527
+ * Hides the spinner loader.
528
+ *
529
+ * @private
530
+ * @returns {void}
531
+ */
532
+ public hideSpinner(): void {
533
+ this.hideWaitingSpinner();
534
+ }
535
+
536
+ private hideWaitingSpinner(): void {
537
+ if (!isNullOrUndefined(this.spinnerElement)) {
538
+ hideSpinner(this.spinnerElement);
539
+ }
540
+ if (!isNullOrUndefined(this.spinnerTemplate) && !isNullOrUndefined(this.spinnerTemplateElement)) {
541
+ detach(this.spinnerTemplateElement);
542
+ }
543
+ }
544
+
545
+ private checkAndUpdateInternalComponent(targetElement: HTMLElement): HTMLElement {
546
+ if (!(this as any).isVue && targetElement.classList.contains('e-richtexteditor')) {
547
+ return targetElement.querySelector('.e-content') as HTMLElement;
548
+ }
549
+
550
+ if ((this as any).isVue && targetElement.nodeName === 'TEXTAREA' && targetElement.classList.contains('e-rte-hidden')) {
551
+ const parentElement = targetElement.parentElement;
552
+ if (parentElement && parentElement.classList.contains('e-richtexteditor')) {
553
+ return parentElement.querySelector('.e-content') as HTMLElement;
554
+ }
555
+ }
556
+
557
+ return targetElement;
558
+ }
559
+
560
+ /**
561
+ * Shows the spinner loader.
562
+ *
563
+ * @returns {void}
564
+ */
565
+ private showWaitingSpinner(): void {
566
+ if (!isNullOrUndefined(this.popupObj)) {
567
+ if (isNullOrUndefined(this.spinnerTemplate) && isNullOrUndefined(this.spinnerElement)) {
568
+ this.spinnerElement = this.popupObj.element;
569
+ createSpinner(
570
+ {
571
+ target: this.spinnerElement,
572
+ width: Browser.isDevice ? '16px' : '14px'
573
+ },
574
+ this.createElement);
575
+ showSpinner(this.spinnerElement);
576
+ }
577
+ if (!isNullOrUndefined(this.spinnerTemplate)) {
578
+ this.setSpinnerTemplate();
579
+ }
580
+ }
581
+ }
582
+
583
+ private keyActionHandler(e: KeyboardEventArgs): void {
584
+ const isNavigation: boolean = (e.action === 'down' || e.action === 'up' || e.action === 'pageUp' || e.action === 'pageDown'
585
+ || e.action === 'home' || e.action === 'end');
586
+ const isTabAction: boolean = e.action === 'tab' || e.action === 'close';
587
+ if (this.list === undefined && !this.isRequested && !isTabAction && e.action !== 'escape' && e.action !== 'space') {
588
+ this.renderList();
589
+ }
590
+ if (isNullOrUndefined(this.list) || (!isNullOrUndefined(this.liCollections) &&
591
+ isNavigation && this.liCollections.length === 0) || this.isRequested) {
592
+ return;
593
+ }
594
+ if (e.action === 'escape') {
595
+ e.preventDefault();
596
+ }
597
+ this.isSelected = e.action === 'escape' ? false : this.isSelected;
598
+ switch (e.action) {
599
+ case 'down':
600
+ case 'up':
601
+ this.isUpDownKey = true;
602
+ this.updateUpDownAction(e);
603
+ break;
604
+ case 'tab':
605
+ if (this.isPopupOpen) {
606
+ e.preventDefault();
607
+ const li: Element = this.list.querySelector('.' + dropDownBaseClasses.selected);
608
+ if (li) {
609
+ this.setSelection(li, e);
610
+ }
611
+ if (this.isPopupOpen) { this.hidePopup(e); }
612
+ }
613
+ break;
614
+ case 'enter':
615
+ if (this.isPopupOpen) {
616
+ e.preventDefault();
617
+ if (this.popupObj && this.popupObj.element.contains(this.selectedLI)) {
618
+ this.updateSelectedItem(this.selectedLI, e, false, true);
619
+ }
620
+ }
621
+ break;
622
+ case 'escape':
623
+ if (this.isPopupOpen) {
624
+ this.hidePopup(e);
625
+ }
626
+ break;
627
+ }
628
+ }
629
+
630
+ private updateUpDownAction(e: KeyboardEventArgs): void {
631
+ const focusEle: Element = this.list.querySelector('.' + dropDownBaseClasses.focus);
632
+ if (this.isSelectFocusItem(focusEle)) {
633
+ this.setSelection(focusEle, e);
634
+ } else if (!isNullOrUndefined(this.liCollections)) {
635
+ const li: Element = this.list.querySelector('.' + dropDownBaseClasses.selected);
636
+ if (!isNullOrUndefined(li)) {
637
+ const value: string | number | boolean = this.getFormattedValue(li.getAttribute('data-value'));
638
+ this.activeIndex = this.getIndexByValue(value);
639
+ }
640
+ let index: number = e.action === 'down' ? this.activeIndex + 1 : this.activeIndex - 1;
641
+ let startIndex: number = 0;
642
+ startIndex = e.action === 'down' && isNullOrUndefined(this.activeIndex) ? 0 : this.liCollections.length - 1;
643
+ index = index < 0 ? this.liCollections.length - 1 : index === this.liCollections.length ? 0 : index;
644
+ const nextItem: Element = isNullOrUndefined(this.activeIndex) ?
645
+ this.liCollections[startIndex as number] : this.liCollections[index as number];
646
+ if (!isNullOrUndefined(nextItem)) {
647
+ this.setSelection(nextItem, e);
648
+ }
649
+ }
650
+ if (this.isPopupOpen) {
651
+ e.preventDefault();
652
+ }
653
+ }
654
+
655
+ private isSelectFocusItem(element: Element): boolean {
656
+ return !isNullOrUndefined(element);
657
+ }
658
+
659
+ private unBindCommonEvent(): void {
660
+ if (!Browser.isDevice) {
661
+ this.keyboardModule.destroy();
662
+ }
663
+ }
664
+
665
+ private onKeyUp(e: KeyboardEventArgs): void {
666
+ let rangetextContent: string[];
667
+ if(this.isUpDownKey && this.isPopupOpen && e.keyCode === 229) {
668
+ this.isUpDownKey = false;
669
+ return;
670
+ }
671
+ this.isTyped = e.code !== 'Enter' && e.code !== 'Space' && e.code !== 'ArrowDown' && e.code !== 'ArrowUp' ? true : false;
672
+ const isRteImage: boolean = document.activeElement.parentElement && document.activeElement.parentElement.querySelector('.e-rte-image') ? true : false;
673
+ if (document.activeElement != this.inputElement && !isRteImage) {
674
+ this.inputElement.focus(); }
675
+ if (this.isContentEditable(this.inputElement)) {
676
+ this.range = this.getCurrentRange();
677
+ rangetextContent = this.range.startContainer.textContent.split('');
678
+ }
679
+ let currentRange: string = this.getTextRange();
680
+ const lastWordRange: string = this.getLastLetter(currentRange);
681
+ const lastTwoLetters: string = this.mentionChar.toString() + this.mentionChar.toString();
682
+ // eslint-disable-next-line security/detect-non-literal-regexp
683
+ const Regex: RegExp = new RegExp(this.mentionChar.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&'), 'g');
684
+ const charRegex: RegExp = new RegExp('[a-zA-Z]', 'g');
685
+ if (e.key === 'Shift' || e.keyCode === 37 || e.keyCode === 39) { return; }
686
+ if (this.beforePopupOpen && this.isPopupOpen && lastWordRange == lastTwoLetters ) {
687
+ this.hidePopup();
688
+ return;
689
+ }
690
+ if ((!currentRange || !lastWordRange) || e.code === 'Enter' || e.keyCode === 27 ||
691
+ (lastWordRange.match(Regex) && lastWordRange.match(Regex).length > 1) ||
692
+ (this.isContentEditable(this.inputElement) && this.range.startContainer &&
693
+ (this.range.startContainer as HTMLElement).previousElementSibling && (this.range.startContainer as HTMLElement).previousElementSibling.tagName !== 'BR' && this.range.startContainer.textContent.split('').length > 0 &&
694
+ (rangetextContent.length === 1 || rangetextContent[rangetextContent.length - 2].indexOf('') === -1 ||
695
+ this.range.startContainer.nodeType === 1))) {
696
+ if (this.allowSpaces && currentRange && currentRange.trim() !== '' && charRegex.test(currentRange) && currentRange.indexOf(this.mentionChar) !== -1
697
+ && !this.isMatchedText() && (currentRange.length > 1 && currentRange.replace(/\u00A0/g, ' ').charAt(currentRange.length - 2) !== ' ') &&
698
+ (this.list && this.list.querySelectorAll('ul').length > 0)) {
699
+ this.queryString = currentRange.substring(currentRange.lastIndexOf(this.mentionChar) + 1).replace('\u00a0', ' ');
700
+ this.searchLists(e);
701
+ } else if (this.isPopupOpen && (!this.allowSpaces || !lastWordRange) && (e.code !== 'ArrowDown' && e.code !== 'ArrowUp')) {
702
+ this.hidePopup();
703
+ this.lineBreak = true;
704
+ }
705
+ return;
706
+ }
707
+ this.queryString = lastWordRange.replace(this.mentionChar, '');
708
+ if (this.mentionChar.charCodeAt(0) === lastWordRange.charCodeAt(0) &&
709
+ this.queryString !== '' && e.keyCode !== 38 && e.keyCode !== 40 && !this.lineBreak) {
710
+ this.searchLists(e);
711
+ if (!this.isPopupOpen && this.queryString.length >= this.minLength) {
712
+ if (!this.isContentEditable(this.inputElement)) {
713
+ this.showPopup();
714
+ } else if (this.isContentEditable(this.inputElement) && this.range && this.range.startContainer !== this.inputElement && e.keyCode !== 9) {
715
+ this.showPopup();
716
+ }
717
+ }
718
+ } else if (lastWordRange.indexOf(this.mentionChar) === 0 && !this.isPopupOpen && e.keyCode !== 8 && (!this.popupObj ||
719
+ (isNullOrUndefined(this.target) && !document.body.contains(this.popupObj.element) ||
720
+ !isNullOrUndefined(this.target) && document.body.contains(this.popupObj.element)))) {
721
+ if (this.initRemoteRender && this.list && this.list.classList.contains('e-nodata')) {
722
+ this.searchLists(e);
723
+ }
724
+ this.resetList(this.dataSource, this.fields);
725
+ if (isNullOrUndefined(this.list)) {
726
+ this.initValue();
727
+ }
728
+ if (!this.isPopupOpen && e.keyCode !== 38 && e.keyCode !== 40 && this.queryString.length >= this.minLength) {
729
+ this.didPopupOpenByTypingInitialChar = true;
730
+ this.showPopup();
731
+ if (this.initRemoteRender && this.list.querySelectorAll('li').length === 0) { this.showWaitingSpinner(); }
732
+ this.lineBreak = false;
733
+ }
734
+ } else if (this.allowSpaces && this.queryString !== '' && currentRange && currentRange.trim() !== '' && currentRange.replace('\u00a0', ' ').lastIndexOf(' ') < currentRange.length - 1 &&
735
+ e.keyCode !== 38 && e.keyCode !== 40 && e.keyCode !== 8 && this.mentionChar.charCodeAt(0) === lastWordRange.charCodeAt(0)) {
736
+ this.queryString = currentRange.substring(currentRange.lastIndexOf(this.mentionChar) + 1).replace('\u00a0', ' ');
737
+ this.searchLists(e);
738
+ } else if (this.queryString === '' && this.isPopupOpen && e.keyCode !== 38 && e.keyCode !== 40 && this.mentionChar.charCodeAt(0) === lastWordRange.charCodeAt(0)) {
739
+ this.searchLists(e);
740
+ if(!this.isListResetted) {
741
+ this.resetList(this.dataSource, this.fields);
742
+ }
743
+ }
744
+ this.isListResetted = false;
745
+ }
746
+
747
+ private isMatchedText(): boolean {
748
+ let isMatched: boolean = false;
749
+ for (let i: number = 0; i < (this.liCollections && this.liCollections.length); i++) {
750
+ if (this.getTextRange() &&
751
+ this.getTextRange().substring(this.getTextRange().lastIndexOf(this.mentionChar) + 1).replace('\u00a0', ' ').trim() === this.liCollections[i as number].getAttribute('data-value').toLowerCase()) {
752
+ isMatched = true;
753
+ }
754
+ }
755
+ return isMatched;
756
+ }
757
+
758
+ private getCurrentRange(): Range {
759
+ this.range = this.inputElement.ownerDocument.getSelection().getRangeAt(0);
760
+ return this.range;
761
+ }
762
+
763
+ private searchLists(e: KeyboardEventArgs | MouseEvent): void {
764
+ this.isDataFetched = false;
765
+ if (isNullOrUndefined(this.list)) {
766
+ super.render();
767
+ this.unWireListEvents();
768
+ this.wireListEvents();
769
+ }
770
+ if (e.type !== 'mousedown' && ((<KeyboardEventArgs>e).keyCode === 40 || (<KeyboardEventArgs>e).keyCode === 38)) {
771
+ this.queryString = this.queryString === '' ? null : this.queryString;
772
+ this.beforePopupOpen = true;
773
+ this.resetList(this.dataSource, this.fields);
774
+ return;
775
+ }
776
+ this.isSelected = false;
777
+ this.activeIndex = null;
778
+ const eventArgs: { [key: string]: Object } = {
779
+ preventDefaultAction: false,
780
+ text: this.queryString,
781
+ updateData: (
782
+ dataSource: { [key: string]: Object }[] | DataManager | string[] | number[], query?: Query,
783
+ fields?: FieldSettingsModel) => {
784
+ if (eventArgs.cancel) {
785
+ return;
786
+ }
787
+ this.isFiltered = true;
788
+ this.filterAction(dataSource, query, fields);
789
+ },
790
+ cancel: false
791
+ };
792
+ this.trigger('filtering', eventArgs, (eventArgs: FilteringEventArgs) => {
793
+ if (!eventArgs.cancel && !this.isFiltered && !eventArgs.preventDefaultAction) {
794
+ this.filterAction(this.dataSource, null, this.fields);
795
+ }
796
+ });
797
+ }
798
+
799
+ private filterAction(
800
+ dataSource: { [key: string]: Object }[] | DataManager | string[] | number[] | boolean[],
801
+ query?: Query, fields?: FieldSettingsModel): void {
802
+ this.beforePopupOpen = true;
803
+ if (this.queryString.length >= this.minLength) {
804
+ this.resetList(dataSource, fields, query);
805
+ this.isListResetted = true;
806
+ } else {
807
+ if (this.isPopupOpen) { this.hidePopup(); }
808
+ this.beforePopupOpen = false;
809
+ }
810
+ this.setDataIndex();
811
+ this.renderReactTemplates();
812
+ }
813
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
814
+ protected onActionComplete(ulElement: HTMLElement, list: { [key: string]: Object }[], e?: Object, isUpdated?: boolean): void {
815
+ super.onActionComplete(ulElement, list, e);
816
+ if (this.isActive) {
817
+ if (!isNullOrUndefined(ulElement)) {
818
+ attributes(ulElement, { 'id': this.inputElement.id + '_options', 'role': 'listbox', 'aria-hidden': 'false' });
819
+ }
820
+ let focusItem: HTMLLIElement = ulElement.querySelector('.' + dropDownBaseClasses.li);
821
+ if (focusItem) {
822
+ focusItem.classList.add(dropDownBaseClasses.selected);
823
+ this.selectedLI = focusItem;
824
+ const value: string | number | boolean = this.getFormattedValue(focusItem.getAttribute('data-value'));
825
+ this.selectEventCallback(focusItem, this.getDataByValue(value), value, true);
826
+ }
827
+ if (this.beforePopupOpen && this.isPopupOpen) {
828
+ if (!isNullOrUndefined(this.popupObj.element)) {
829
+ this.popupObj.element.remove();
830
+ }
831
+ this.renderPopup();
832
+ }
833
+ }
834
+ }
835
+ private setDataIndex(): void {
836
+ for (let i: number = 0; this.liCollections && i < this.liCollections.length; i++) {
837
+ this.liCollections[i as number].setAttribute('data-index', i.toString());
838
+ }
839
+ }
840
+
841
+ protected listOption(dataSource: { [key: string]: Object }[], fieldsSettings: FieldSettingsModel): FieldSettingsModel {
842
+ const fields: { [key: string]: Object } = <{ [key: string]: Object }>super.listOption(dataSource, fieldsSettings);
843
+ if (isNullOrUndefined(fields.itemCreated)) {
844
+ fields.itemCreated = (e: { [key: string]: HTMLElement }) => {
845
+ if (this.highlight) {
846
+ if (this.inputElement.tagName === this.getNgDirective() && this.itemTemplate) {
847
+ setTimeout((): void => {
848
+ highlightSearch(e.item, this.queryString, this.ignoreCase, this.filterType);
849
+ }, 0);
850
+ } else {
851
+ highlightSearch(e.item, this.queryString, this.ignoreCase, this.filterType);
852
+ }
853
+ }
854
+ };
855
+ } else {
856
+ const itemCreated: Function = <Function>fields.itemCreated;
857
+ fields.itemCreated = (e: { [key: string]: HTMLElement }) => {
858
+ if (this.highlight) {
859
+ highlightSearch(e.item, this.queryString, this.ignoreCase, this.filterType);
860
+ }
861
+ itemCreated.apply(this, [e]);
862
+ };
863
+ }
864
+ return fields;
865
+ }
866
+
867
+ private elementValue(): string {
868
+ if (!this.isContentEditable(this.inputElement)) {
869
+ return (this.inputElement as HTMLInputElement | HTMLTextAreaElement).value.replace(this.mentionChar, '');
870
+ } else {
871
+ return (this.inputElement as HTMLElement).textContent.replace(this.mentionChar, '');
872
+ }
873
+ }
874
+
875
+ protected getQuery(query: Query): Query {
876
+ const filterQuery: Query = query ? query.clone() : this.query ? this.query.clone() : new Query();
877
+ const filterType: string = (this.queryString === '' && !isNullOrUndefined(this.elementValue())) ? 'equal' : this.filterType;
878
+ const queryString: string = (this.queryString === '' && !isNullOrUndefined(this.elementValue())) ?
879
+ this.elementValue() : this.queryString;
880
+ if (this.isFiltered) {
881
+ return filterQuery;
882
+ }
883
+ if (this.queryString !== null && this.queryString !== '') {
884
+ const dataType: string = <string>this.typeOfData(this.dataSource as { [key: string]: Object }[]).typeof;
885
+ if (!(this.dataSource instanceof DataManager) && dataType === 'string' || dataType === 'number') {
886
+ filterQuery.where('', filterType, queryString, this.ignoreCase, this.ignoreAccent);
887
+ } else {
888
+ const mapping: string = !isNullOrUndefined(this.fields.text) ? this.fields.text : '';
889
+ filterQuery.where(mapping, filterType, queryString, this.ignoreCase, this.ignoreAccent);
890
+ }
891
+ }
892
+ if (!isNullOrUndefined(this.suggestionCount)) {
893
+ // Since defualt value of suggestioncount is 25, checked the condition
894
+ if (this.suggestionCount !== 25) {
895
+ for (let queryElements: number = 0; queryElements < filterQuery.queries.length; queryElements++) {
896
+ if (filterQuery.queries[queryElements as number].fn === 'onTake') {
897
+ filterQuery.queries.splice(queryElements, 1);
898
+ }
899
+ }
900
+ }
901
+ filterQuery.take(this.suggestionCount);
902
+ }
903
+ return filterQuery;
904
+ }
905
+
906
+ private renderHightSearch(): void {
907
+ if (this.highlight) {
908
+ for (let i: number = 0; i < this.liCollections.length; i++) {
909
+ const isHighlight: HTMLElement = this.ulElement.querySelector('.e-active');
910
+ if (!isHighlight) {
911
+ revertHighlightSearch(this.liCollections[i as number]);
912
+ highlightSearch(this.liCollections[i as number], this.queryString, this.ignoreCase, this.filterType);
913
+ }
914
+ }
915
+ }
916
+ }
917
+
918
+ private getTextRange(): string {
919
+ let text: string;
920
+ if (!this.isContentEditable(this.inputElement)) {
921
+ const component: HTMLInputElement | HTMLTextAreaElement = (this.inputElement as HTMLInputElement | HTMLTextAreaElement);
922
+ if (!isNullOrUndefined(component)) {
923
+ const startPos: number = component.selectionStart;
924
+ if (component.value && startPos >= 0) {
925
+ text = component.value.substring(0, startPos);
926
+ }
927
+ }
928
+ } else {
929
+ if (this.range) {
930
+ const selectedElem: Node = this.range.startContainer;
931
+ if (!isNullOrUndefined(selectedElem)) {
932
+ const workingNodeContent: string = selectedElem.textContent;
933
+ const selectStartOffset: number = this.range.startOffset;
934
+ if (workingNodeContent && selectStartOffset >= 0) {
935
+ text = workingNodeContent.substring(0, selectStartOffset);
936
+ }
937
+ }
938
+ }
939
+ }
940
+ return text;
941
+ }
942
+
943
+ private getLastLetter(text: string): string {
944
+ if (isNullOrUndefined(text)) {return ''; }
945
+ const textValue: string = text.replace(/\u00A0/g, ' ');
946
+ const words: string[] = textValue.split(/\s+/);
947
+ const wordCnt: number = words.length - 1;
948
+ return words[wordCnt as number].trim();
949
+ }
950
+
951
+ private isContentEditable(element: HTMLInputElement | HTMLTextAreaElement | HTMLElement): boolean {
952
+ return element && element.nodeName !== 'INPUT' && element.nodeName !== 'TEXTAREA';
953
+ }
954
+
955
+ /**
956
+ * Opens the popup that displays the list of items.
957
+ *
958
+ * @returns {void}
959
+ */
960
+ public showPopup(): void {
961
+ this.beforePopupOpen = true;
962
+ if (document.activeElement != this.inputElement) {
963
+ this.inputElement.focus();
964
+ }
965
+ this.queryString = this.didPopupOpenByTypingInitialChar ? this.queryString : '';
966
+ this.didPopupOpenByTypingInitialChar = false;
967
+ if (this.isContentEditable(this.inputElement)) {
968
+ this.range = this.getCurrentRange();
969
+ }
970
+ if (!this.isTyped) {
971
+ this.resetList(this.dataSource, this.fields);
972
+ }
973
+ if (isNullOrUndefined(this.list)) {
974
+ this.initValue();
975
+ }
976
+ this.renderPopup();
977
+ attributes(this.inputElement, { 'aria-activedescendant': this.selectedElementID});
978
+ if (this.selectedElementID == null)
979
+ {
980
+ this.inputElement.removeAttribute('aria-activedescendant');
981
+ }
982
+ }
983
+
984
+ /* eslint-disable valid-jsdoc, jsdoc/require-param */
985
+ /**
986
+ * Hides the popup if it is in an open state.
987
+ *
988
+ * @returns {void}
989
+ */
990
+ public hidePopup(e?: MouseEvent | KeyboardEventArgs): void {
991
+ this.removeSelection();
992
+ this.closePopup(0, e);
993
+ }
994
+
995
+ private closePopup(delay: number, e: MouseEvent | KeyboardEventArgs): void {
996
+ if (!(this.popupObj && document.body.contains(this.popupObj.element) && this.beforePopupOpen)) {
997
+ return;
998
+ }
999
+ EventHandler.remove(document, 'mousedown', this.onDocumentClick);
1000
+ this.inputElement.removeAttribute('aria-owns');
1001
+ this.inputElement.removeAttribute('aria-activedescendant');
1002
+ this.beforePopupOpen = false;
1003
+ const animModel: AnimationModel = {
1004
+ name: 'FadeOut',
1005
+ duration: 100,
1006
+ delay: delay ? delay : 0
1007
+ };
1008
+ const popupInstance: Popup = this.popupObj;
1009
+ const eventArgs: PopupEventArgs = { popup: popupInstance, cancel: false, animation: animModel , event: e || null};
1010
+ this.trigger('closed', eventArgs, (eventArgs: PopupEventArgs) => {
1011
+ if (!eventArgs.cancel && this.popupObj) {
1012
+ if (this.isPopupOpen) {
1013
+ this.popupObj.hide(new Animation(eventArgs.animation));
1014
+ } else {
1015
+ this.destroyPopup();
1016
+ }
1017
+ }
1018
+ });
1019
+ }
1020
+
1021
+ private renderPopup(): void {
1022
+ const args: BeforeOpenEventArgs = { cancel: false };
1023
+ this.trigger('beforeOpen', args, (args: BeforeOpenEventArgs) => {
1024
+ if (!args.cancel) {
1025
+ let popupEle: HTMLElement;
1026
+ if (isNullOrUndefined(this.target)) {
1027
+ popupEle = this.createElement('div', {
1028
+ id: this.inputElement.id + '_popup', className: 'e-mention e-popup ' + (this.cssClass != null ? this.cssClass : '')
1029
+ });
1030
+ } else {
1031
+ popupEle = this.element;
1032
+ if (this.cssClass != null) { addClass([popupEle], this.cssClass.split(' ')); }
1033
+ }
1034
+ if (!isNullOrUndefined(this.target)) {
1035
+ popupEle.id = this.inputElement.id + '_popup';
1036
+ }
1037
+ this.listHeight = formatUnit(this.popupHeight);
1038
+ if (!isNullOrUndefined(this.list.querySelector('li')) && !this.initRemoteRender) {
1039
+ const li: HTMLLIElement = this.list.querySelector('.' + dropDownBaseClasses.focus);
1040
+ if (!isNullOrUndefined(li)) {
1041
+ this.selectedLI = li;
1042
+ const value: string | number | boolean = this.getFormattedValue(li.getAttribute('data-value'));
1043
+ this.selectEventCallback(li, this.getDataByValue(value), value, true);
1044
+ }
1045
+ }
1046
+ append([this.list], popupEle);
1047
+ if (this.inputElement.parentElement && this.inputElement.parentElement.parentElement &&
1048
+ this.inputElement.parentElement.parentElement.classList.contains('e-richtexteditor')) {
1049
+ if (popupEle.firstElementChild && popupEle.firstElementChild.childElementCount > 0) {
1050
+ popupEle.firstElementChild.setAttribute('aria-owns', this.inputElement.parentElement.parentElement.id);
1051
+ }
1052
+ }
1053
+ if ((!this.popupObj || !document.body.contains(this.popupObj.element)) ||
1054
+ !document.contains(popupEle) && isNullOrUndefined(this.target)) {
1055
+ document.body.appendChild(popupEle);
1056
+ }
1057
+ let coordinates: { [key: string]: number };
1058
+ popupEle.style.visibility = 'hidden';
1059
+ this.setHeight(popupEle);
1060
+ const offsetValue: number = 0;
1061
+ const left: number = 0;
1062
+ this.initializePopup(popupEle, offsetValue, left);
1063
+ this.checkCollision(popupEle);
1064
+ popupEle.style.visibility = 'visible';
1065
+ let popupLeft: number = popupEle.parentElement.offsetWidth - popupEle.offsetWidth;
1066
+ let popupHeight: number = popupEle.offsetHeight;
1067
+ addClass([popupEle], ['e-mention' , 'e-popup', 'e-popup-close']);
1068
+ if (!isNullOrUndefined(this.list)) {
1069
+ this.unWireListEvents(); this.wireListEvents();
1070
+ }
1071
+ this.selectedElementID = this.selectedLI ? this.selectedLI.id : null;
1072
+ attributes(this.inputElement, { 'aria-owns': this.inputElement.id + '_options', 'aria-activedescendant': this.selectedElementID });
1073
+ if (this.selectedElementID == null)
1074
+ {
1075
+ this.inputElement.removeAttribute('aria-activedescendant');
1076
+ }
1077
+ const animModel: AnimationModel = { name: 'FadeIn', duration: 100 };
1078
+ this.beforePopupOpen = true;
1079
+ const popupInstance: Popup = this.popupObj;
1080
+ const eventArgs: PopupEventArgs = { popup: popupInstance, cancel: false, animation: animModel };
1081
+ this.trigger('opened', eventArgs, (eventArgs: PopupEventArgs) => {
1082
+ if (!eventArgs.cancel) {
1083
+ this.renderReactTemplates();
1084
+ if (this.popupObj) {
1085
+ this.popupObj.show(new Animation(eventArgs.animation), (this.zIndex === 1000) ? this.inputElement : null);
1086
+ }
1087
+ if (isNullOrUndefined(this.getTriggerCharPosition())) { return; }
1088
+ coordinates = this.getCoordinates(this.inputElement, this.getTriggerCharPosition());
1089
+ if (!this.isCollided) {
1090
+ popupEle.style.cssText = 'top: '.concat(coordinates.top.toString(), 'px;\n left: ').concat(coordinates.left.toString(), 'px;\nposition: absolute;\n display: block;');
1091
+ } else {
1092
+ if(this.collision.length > 0 && this.collision.indexOf('right') > -1 && this.collision.indexOf('bottom') === -1) {
1093
+ popupEle.style.cssText = 'top: '.concat(coordinates.top.toString(), 'px;\n left: ').concat(popupLeft.toString(), 'px;\nposition: absolute;\n display: block;');
1094
+ }
1095
+ else if(this.collision && this.collision.length > 0 && this.collision.indexOf('bottom') > -1 && this.collision.indexOf('right') === -1) {
1096
+ popupEle.style.left = formatUnit(coordinates.left);
1097
+ popupEle.style.top = formatUnit(coordinates.top - parseInt(popupHeight.toString()));
1098
+ }
1099
+ else if(this.collision && this.collision.length > 0 && this.collision.indexOf('bottom') > -1 && this.collision.indexOf('right') > -1) {
1100
+ popupEle.style.left = formatUnit(popupLeft);
1101
+ popupEle.style.top = formatUnit(coordinates.top - parseInt(popupHeight.toString()));
1102
+ }
1103
+ else {
1104
+ popupEle.style.left = formatUnit(coordinates.left);
1105
+ popupEle.style.top = formatUnit(coordinates.top - parseInt(this.popupHeight.toString()));
1106
+ }
1107
+ this.isCollided = false;
1108
+ this.collision = [];
1109
+ }
1110
+ popupEle.style.width = this.popupWidth !== '100%' && !isNullOrUndefined(this.popupWidth) ? formatUnit(this.popupWidth) : 'auto';
1111
+ this.setHeight(popupEle);
1112
+ popupEle.style.zIndex = this.zIndex === 1000 ? getZindexPartial(popupEle).toString() : this.zIndex.toString();
1113
+ } else {
1114
+ this.beforePopupOpen = false;
1115
+ this.destroyPopup();
1116
+ }
1117
+ });
1118
+ } else {
1119
+ this.beforePopupOpen = false;
1120
+ }
1121
+ });
1122
+ }
1123
+
1124
+ private setHeight(popupEle: HTMLElement): void {
1125
+ if (this.popupHeight !== 'auto' && this.list) {
1126
+ this.list.style.maxHeight = (parseInt(this.listHeight, 10) - 2).toString() + 'px'; // due to box-sizing property
1127
+ popupEle.style.maxHeight = formatUnit(this.popupHeight);
1128
+ } else {
1129
+ popupEle.style.height = 'auto';
1130
+ }
1131
+ }
1132
+
1133
+ private checkCollision(popupEle: HTMLElement): void {
1134
+ if (!Browser.isDevice || (Browser.isDevice && !(this.getModuleName() === 'mention'))) {
1135
+ let coordinates: { [key: string]: number } = this.getCoordinates(this.inputElement, this.getTriggerCharPosition());
1136
+ this.collision = isCollide(popupEle, null, coordinates.left, coordinates.top);
1137
+ if (this.collision.length > 0) {
1138
+ popupEle.style.marginTop = -parseInt(getComputedStyle(popupEle).marginTop, 10) + 'px';
1139
+ this.isCollided = true;
1140
+ }
1141
+ this.popupObj.resolveCollision();
1142
+ }
1143
+ }
1144
+
1145
+ private getTriggerCharPosition(): number {
1146
+ let mostRecentTriggerCharPos: number;
1147
+ const currentRange: string = this.getTextRange();
1148
+ if (currentRange !== undefined && currentRange !== null) {
1149
+ mostRecentTriggerCharPos = 0;
1150
+ const idx: number = currentRange.lastIndexOf(this.mentionChar);
1151
+ if (idx >= mostRecentTriggerCharPos) {
1152
+ mostRecentTriggerCharPos = idx;
1153
+ }
1154
+ }
1155
+ return mostRecentTriggerCharPos ? mostRecentTriggerCharPos : 0;
1156
+ }
1157
+
1158
+ private initializePopup(element: HTMLElement, offsetValue: number, left: number): void {
1159
+ this.popupObj = new Popup(element, {
1160
+ width: this.setWidth(), targetType: 'relative',
1161
+ relateTo: this.inputElement, collision: { X: 'flip', Y: 'flip' }, offsetY: offsetValue,
1162
+ enableRtl: this.enableRtl, offsetX: left, position: { X: 'left', Y: 'bottom' }, actionOnScroll: 'hide',
1163
+ zIndex: this.zIndex,
1164
+ close: () => {
1165
+ this.destroyPopup();
1166
+ },
1167
+ open: () => {
1168
+ EventHandler.add(document, 'mousedown', this.onDocumentClick, this);
1169
+ this.isPopupOpen = true;
1170
+ this.setDataIndex();
1171
+ }
1172
+ });
1173
+ }
1174
+
1175
+ private setWidth(): string {
1176
+ let width: string = formatUnit(this.popupWidth);
1177
+ if (width.indexOf('%') > -1) {
1178
+ const inputWidth: number = this.inputElement.offsetWidth * parseFloat(width) / 100;
1179
+ width = inputWidth.toString() + 'px';
1180
+ }
1181
+ return width;
1182
+ }
1183
+
1184
+ private destroyPopup(): void {
1185
+ this.isPopupOpen = false;
1186
+ this.popupObj.destroy();
1187
+ if (isNullOrUndefined(this.target)) {
1188
+ detach(this.popupObj.element);
1189
+ } else {
1190
+ this.popupObj.element.innerHTML = '';
1191
+ this.popupObj.element.removeAttribute('style');
1192
+ this.popupObj.element.removeAttribute('aria-disabled');
1193
+ }
1194
+ }
1195
+
1196
+ private onDocumentClick(e: MouseEvent): void {
1197
+ const target: HTMLElement = <HTMLElement>e.target;
1198
+ if (!(!isNullOrUndefined(this.popupObj) && closest(target, '#' + this.popupObj.element.id))) {
1199
+ this.hidePopup(e);
1200
+ }
1201
+ }
1202
+
1203
+ private getCoordinates(element: HTMLInputElement | HTMLTextAreaElement | HTMLElement, position: number): { [key: string]: number } {
1204
+ const properties: string[] = ['direction', 'boxSizing', 'width', 'height', 'overflowX', 'overflowY', 'borderTopWidth', 'borderRightWidth', 'borderBottomWidth', 'borderLeftWidth', 'paddingTop', 'paddingRight', 'paddingBottom', 'paddingLeft', 'fontStyle', 'fontVariant', 'fontWeight', 'fontStretch', 'fontSize', 'fontSizeAdjust', 'lineHeight', 'fontFamily', 'textAlign', 'textTransform', 'textIndent', 'textDecoration', 'letterSpacing', 'wordSpacing'];
1205
+ let div: HTMLElement;
1206
+ let span: HTMLElement;
1207
+ let range: Range;
1208
+ let globalRange: Range;
1209
+ let coordinates: { [key: string]: number };
1210
+ let computed: CSSStyleDeclaration;
1211
+ let rect: ClientRect;
1212
+ if (!this.isContentEditable(this.inputElement)) {
1213
+ div = this.createElement('div', { className: 'e-form-mirror-div'});
1214
+ document.body.appendChild(div);
1215
+ computed = getComputedStyle((element as HTMLInputElement | HTMLTextAreaElement));
1216
+ div.style.position = 'absolute';
1217
+ div.style.visibility = 'hidden';
1218
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
1219
+ properties.forEach((prop: any) => {
1220
+ // eslint-disable-next-line security/detect-object-injection
1221
+ div.style[prop] = computed[prop];
1222
+ });
1223
+ div.textContent = (element as HTMLInputElement | HTMLTextAreaElement).value.substring(0, position);
1224
+ if (this.inputElement.nodeName === 'INPUT') {
1225
+ div.textContent = div.textContent.replace(/\s/g, '\u00a0');
1226
+ }
1227
+ span = this.createElement('span');
1228
+ span.textContent = (element as HTMLInputElement | HTMLTextAreaElement).value.substring(position) || '.';
1229
+ div.appendChild(span);
1230
+ rect = (element as HTMLInputElement | HTMLTextAreaElement).getBoundingClientRect();
1231
+ } else {
1232
+ const selectedNodePosition: number = this.getTriggerCharPosition();
1233
+ globalRange = this.range;
1234
+ range = document.createRange();
1235
+ if (this.getTextRange() && this.getTextRange().lastIndexOf(this.mentionChar) !== -1) {
1236
+ range.setStart(globalRange.startContainer, selectedNodePosition);
1237
+ range.setEnd(globalRange.startContainer, selectedNodePosition);
1238
+ }
1239
+ else {
1240
+ range.setStart(globalRange.startContainer, globalRange.startOffset);
1241
+ range.setEnd(globalRange.startContainer, globalRange.endOffset);
1242
+ }
1243
+ this.isTyped = false;
1244
+ range.collapse(false);
1245
+ rect = range.getBoundingClientRect().top === 0 ? (range.startContainer as any).getClientRects()[0] : range.getBoundingClientRect();
1246
+ }
1247
+ const doc: HTMLElement = document.documentElement;
1248
+ const windowLeft: number = (window.pageXOffset || doc.scrollLeft) - (doc.clientLeft || 0);
1249
+ const windowTop: number = (window.pageYOffset || doc.scrollTop) - (doc.clientTop || 0);
1250
+ let width: number = 0;
1251
+ if (!isNullOrUndefined(range) && range.getBoundingClientRect().top === 0) {
1252
+ for (let i = 0; i < this.range.startContainer.childNodes.length; i++) {
1253
+ if (this.range.startContainer.childNodes[i as number].nodeType !== Node.TEXT_NODE && this.range.startContainer.childNodes[i as number].textContent.trim() !== '') {
1254
+ width += (this.range.startContainer.childNodes[i as number] as any).getClientRects()[0].width;
1255
+ }
1256
+ else if (this.range.startContainer.childNodes[i as number].textContent !== '') {
1257
+ let span = document.createElement("span");
1258
+ span.innerHTML = this.range.startContainer.childNodes[i as number].nodeValue;
1259
+ document.body.appendChild(span);
1260
+ let textNodeWidth : number = span.offsetWidth;
1261
+ document.body.removeChild(span);
1262
+ width += textNodeWidth;
1263
+ }
1264
+ }
1265
+ }
1266
+ if (!this.isContentEditable(this.inputElement)) {
1267
+ coordinates = {
1268
+ top: rect.top + windowTop + span.offsetTop + parseInt(computed.borderTopWidth, 10) +
1269
+ parseInt(computed.fontSize, 10) + 3 - (element as HTMLInputElement | HTMLTextAreaElement).scrollTop - (this.isCollided ? 10 : 0),
1270
+ left: rect.left + windowLeft + span.offsetLeft + parseInt(computed.borderLeftWidth, 10)
1271
+ };
1272
+ document.body.removeChild(div);
1273
+ } else {
1274
+ if (this.collision && this.collision.length > 0 && this.collision.indexOf('right') > -1 && this.collision.indexOf('bottom') === -1) {
1275
+ coordinates = {
1276
+ top: rect.top + windowTop + parseInt(getComputedStyle(this.inputElement).fontSize, 10),
1277
+ left: rect.left + windowLeft + width
1278
+ };
1279
+ }
1280
+ else {
1281
+ coordinates = {
1282
+ top: rect.top + windowTop + parseInt(getComputedStyle(this.inputElement).fontSize, 10) - (this.isCollided ? 10 : 0),
1283
+ left: rect.left + windowLeft + width
1284
+ };
1285
+ }
1286
+ }
1287
+ return coordinates;
1288
+ }
1289
+
1290
+ private initValue(): void {
1291
+ this.renderList();
1292
+ if (this.dataSource instanceof DataManager) {
1293
+ this.initRemoteRender = true;
1294
+ } else {
1295
+ this.updateValues();
1296
+ }
1297
+ }
1298
+
1299
+ private updateValues(): void {
1300
+ const li: HTMLElement = this.list.querySelector('.' + dropDownBaseClasses.focus);
1301
+ if (!isNullOrUndefined(li)) {
1302
+ this.setSelection(li, null);
1303
+ }
1304
+ }
1305
+
1306
+ protected renderList(): void {
1307
+ super.render();
1308
+ this.unWireListEvents();
1309
+ this.wireListEvents();
1310
+ }
1311
+
1312
+ /**
1313
+ * Event binding for list
1314
+ *
1315
+ * @returns {void}
1316
+ */
1317
+ private wireListEvents(): void {
1318
+ EventHandler.add(this.list, 'click', this.onMouseClick, this);
1319
+ EventHandler.add(this.list, 'mouseover', this.onMouseOver, this);
1320
+ EventHandler.add(this.list, 'mouseout', this.onMouseLeave, this);
1321
+ }
1322
+
1323
+ /**
1324
+ * Event un binding for list items.
1325
+ *
1326
+ * @returns {void}
1327
+ */
1328
+ private unWireListEvents(): void {
1329
+ EventHandler.remove(this.list, 'click', this.onMouseClick);
1330
+ EventHandler.remove(this.list, 'mouseover', this.onMouseOver);
1331
+ EventHandler.remove(this.list, 'mouseout', this.onMouseLeave);
1332
+ }
1333
+
1334
+ private onMouseClick(e: MouseEvent): void {
1335
+ const target: Element = <Element>e.target;
1336
+ const li: HTMLElement = <HTMLElement>closest(target, '.' + dropDownBaseClasses.li);
1337
+ if (!this.isValidLI(li)) {
1338
+ return;
1339
+ }
1340
+ this.isSelected = true;
1341
+ this.setSelection(li, e);
1342
+ const delay: number = 100;
1343
+ this.closePopup(delay, e);
1344
+ this.inputElement.focus();
1345
+ }
1346
+
1347
+ private updateSelectedItem(
1348
+ li: Element,
1349
+ e: MouseEvent | KeyboardEvent | TouchEvent,
1350
+ preventSelect?: boolean,
1351
+ isSelection?: boolean): void {
1352
+ this.removeSelection();
1353
+ li.classList.add(dropDownBaseClasses.selected);
1354
+ this.removeHover();
1355
+ const value: string | number | boolean = this.getFormattedValue(li.getAttribute('data-value'));
1356
+ const selectedData: string | number | boolean | {
1357
+ [key: string]: Object
1358
+ } = this.getDataByValue(value);
1359
+ if (!preventSelect && !isNullOrUndefined(e) && !((e as KeyboardEventArgs).action === "down" || (e as KeyboardEventArgs).action === "up")) {
1360
+ const items: FieldSettingsModel = this.detachChanges(selectedData);
1361
+ this.isSelected = true;
1362
+ const eventArgs: SelectEventArgs = {
1363
+ e: e,
1364
+ item: li as HTMLLIElement,
1365
+ itemData: items,
1366
+ isInteracted: e ? true : false,
1367
+ cancel: false
1368
+ };
1369
+ this.trigger('select', eventArgs, (eventArgs: SelectEventArgs) => {
1370
+ if (eventArgs.cancel) {
1371
+ li.classList.remove(dropDownBaseClasses.selected);
1372
+ this.isSelected = false;
1373
+ this.isSelectCancel = true;
1374
+ } else {
1375
+ this.selectEventCallback(li, selectedData, value);
1376
+ if (isSelection) {
1377
+ this.setSelectOptions(li, e);
1378
+ }
1379
+ }
1380
+ });
1381
+ } else {
1382
+ this.selectEventCallback(li, selectedData, value);
1383
+ if (isSelection) {
1384
+ this.setSelectOptions(li, e);
1385
+ }
1386
+ }
1387
+ }
1388
+
1389
+ private setSelection(li: Element, e: MouseEvent | KeyboardEventArgs | TouchEvent): void {
1390
+ if (this.isValidLI(li) && (!li.classList.contains(dropDownBaseClasses.selected) || (this.isPopupOpen && this.isSelected
1391
+ && li.classList.contains(dropDownBaseClasses.selected)))) {
1392
+ this.updateSelectedItem(li, e, false, true);
1393
+ } else {
1394
+ this.setSelectOptions(li, e);
1395
+ }
1396
+ }
1397
+
1398
+ private setSelectOptions(li: Element, e?: MouseEvent | KeyboardEventArgs | KeyboardEvent | TouchEvent): void {
1399
+ if (this.list) {
1400
+ this.removeHover();
1401
+ }
1402
+ this.previousSelectedLI = (!isNullOrUndefined(this.selectedLI)) ? this.selectedLI : null;
1403
+ this.selectedLI = li as HTMLLIElement;
1404
+ if (this.isPopupOpen && !isNullOrUndefined(this.selectedLI)) {
1405
+ this.setScrollPosition(e as KeyboardEventArgs);
1406
+ }
1407
+ if (e && ((e as KeyboardEventArgs).keyCode === 38 || (e as KeyboardEventArgs).keyCode === 40)) { return; }
1408
+ if (isNullOrUndefined(e) || this.setValue(e as KeyboardEventArgs)) {
1409
+ return;
1410
+ }
1411
+ }
1412
+
1413
+ private setScrollPosition(e?: KeyboardEventArgs): void {
1414
+ if (!isNullOrUndefined(e)) {
1415
+ switch (e.action) {
1416
+ case 'pageDown':
1417
+ case 'down':
1418
+ case 'end':
1419
+ this.scrollBottom();
1420
+ break;
1421
+ default:
1422
+ this.scrollTop();
1423
+ break;
1424
+ }
1425
+ } else {
1426
+ this.scrollBottom(true);
1427
+ }
1428
+ }
1429
+
1430
+ private scrollBottom(isInitial?: boolean): void {
1431
+ if (!isNullOrUndefined(this.selectedLI)) {
1432
+ const currentOffset: number = this.list.offsetHeight;
1433
+ const nextBottom: number = this.selectedLI.offsetTop + this.selectedLI.offsetHeight - this.list.scrollTop;
1434
+ let nextOffset: number = this.list.scrollTop + nextBottom - currentOffset;
1435
+ nextOffset = isInitial ? nextOffset + parseInt(getComputedStyle(this.list).paddingTop, 10) * 2 : nextOffset;
1436
+ const boxRange: number = this.selectedLI.offsetTop + this.selectedLI.offsetHeight - this.list.scrollTop;
1437
+ if (this.activeIndex === 0) {
1438
+ this.list.scrollTop = 0;
1439
+ } else if (nextBottom > currentOffset || !(boxRange > 0 && this.list.offsetHeight > boxRange)) {
1440
+ this.list.scrollTop = nextOffset;
1441
+ }
1442
+ }
1443
+ }
1444
+
1445
+ private scrollTop(): void {
1446
+ if (!isNullOrUndefined(this.selectedLI)) {
1447
+ let nextOffset: number = this.selectedLI.offsetTop - this.list.scrollTop;
1448
+ nextOffset = this.fields.groupBy && nextOffset;
1449
+ const boxRange: number = (this.selectedLI.offsetTop + this.selectedLI.offsetHeight - this.list.scrollTop);
1450
+ if (this.activeIndex === 0) {
1451
+ this.list.scrollTop = 0;
1452
+ } else if (nextOffset < 0) {
1453
+ this.list.scrollTop = this.list.scrollTop + nextOffset;
1454
+ } else if (!(boxRange > 0 && this.list.offsetHeight > boxRange)) {
1455
+ this.list.scrollTop = this.selectedLI.offsetTop;
1456
+ }
1457
+ }
1458
+ }
1459
+
1460
+ private selectEventCallback(
1461
+ li: Element,
1462
+ selectedData?: string | number | boolean | { [key: string]: Object },
1463
+ value?: string | number | boolean, selectLi?: boolean): void {
1464
+ this.previousItemData = (!isNullOrUndefined(this.itemData)) ? this.itemData : null;
1465
+ this.item = li as HTMLLIElement;
1466
+ this.itemData = selectedData;
1467
+ const focusedItem: Element = this.list.querySelector('.' + dropDownBaseClasses.focus);
1468
+ if (focusedItem) {
1469
+ removeClass([focusedItem], dropDownBaseClasses.focus);
1470
+ }
1471
+ if (selectLi) {
1472
+ addClass([li], dropDownBaseClasses.selected);
1473
+ }
1474
+ li.setAttribute('aria-selected', 'true');
1475
+ this.activeIndex = this.getIndexByValue(value);
1476
+ }
1477
+
1478
+ private detachChanges(value: string | number | boolean | {
1479
+ [key: string]: Object
1480
+ }): FieldSettingsModel {
1481
+ let items: FieldSettingsModel;
1482
+ if (typeof value === 'string' ||
1483
+ typeof value === 'boolean' ||
1484
+ typeof value === 'number') {
1485
+ items = Object.defineProperties({}, {
1486
+ value: {
1487
+ value: value,
1488
+ enumerable: true
1489
+ },
1490
+ text: {
1491
+ value: value,
1492
+ enumerable: true
1493
+ }
1494
+ });
1495
+ } else {
1496
+ items = value;
1497
+ }
1498
+ return items;
1499
+ }
1500
+
1501
+ private setValue(e?: KeyboardEventArgs | MouseEvent): boolean {
1502
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
1503
+ if (!(this as any).isReact) {
1504
+ if (!isNullOrUndefined(this.displayTemplate)) {
1505
+ this.setDisplayTemplate();
1506
+ }
1507
+ this.updateMentionValue(e);
1508
+ return true;
1509
+ }
1510
+ else {
1511
+ if (!isNullOrUndefined(this.displayTemplate)) {
1512
+ this.setDisplayTemplate(e);
1513
+ } else {
1514
+ this.updateMentionValue(e);
1515
+ }
1516
+ return true;
1517
+ }
1518
+ }
1519
+
1520
+ private updateMentionValue(e?: KeyboardEventArgs | MouseEvent): void {
1521
+ const dataItem: { [key: string]: string } = this.getItemData();
1522
+ let textSuffix: string;
1523
+ let value: string;
1524
+ let endPos: number;
1525
+ let range: Range;
1526
+ let globalRange: Range;
1527
+ const selection: Selection = this.inputElement.ownerDocument.getSelection();
1528
+ const startPos: number = this.getTriggerCharPosition();
1529
+ textSuffix = typeof this.suffixText === 'string' ? this.suffixText : '';
1530
+ if (this.isSelectCancel) {
1531
+ this.isSelectCancel = false;
1532
+ return;
1533
+ }
1534
+ if (dataItem.text !== null) {
1535
+ value = this.mentionVal(dataItem.text);
1536
+ }
1537
+ if (!this.isContentEditable(this.inputElement)) {
1538
+ const myField: HTMLInputElement | HTMLTextAreaElement = this.inputElement as HTMLInputElement | HTMLTextAreaElement;
1539
+ const currentTriggerSnippet: string =
1540
+ this.getTextRange().substring(startPos + this.mentionChar.length, this.getTextRange().length);
1541
+ value += textSuffix;
1542
+ endPos = startPos + this.mentionChar.length;
1543
+ endPos += currentTriggerSnippet.length;
1544
+ myField.value = myField.value.substring(0, startPos) + value + myField.value.substring(endPos, myField.value.length);
1545
+ myField.selectionStart = startPos + value.length;
1546
+ myField.selectionEnd = startPos + value.length;
1547
+ if (this.isPopupOpen) { this.hidePopup(); }
1548
+ //New event to update the RichTextEditor value, when a mention item is selected using mouse click action.
1549
+ if (!isNullOrUndefined((e as PointerEvent).pointerType) && (e as PointerEvent).pointerType === 'mouse') {
1550
+ const event: Event = new CustomEvent('content-changed', { detail: { click: true } });
1551
+ this.inputElement.dispatchEvent(event);
1552
+ };
1553
+ this.onChangeEvent(e);
1554
+ } else {
1555
+ endPos = this.getTriggerCharPosition() + this.mentionChar.length;
1556
+ if (this.range && (this.range.startContainer.textContent.trim() !== this.mentionChar)) {
1557
+ endPos = this.range.endOffset;
1558
+ }
1559
+ globalRange = this.range;
1560
+ range = document.createRange();
1561
+ if (((this.getTextRange() && this.getTextRange().lastIndexOf(this.mentionChar) !== -1) || this.getTextRange() && this.getTextRange().trim() === this.mentionChar)) {
1562
+ range.setStart(globalRange.startContainer, startPos);
1563
+ range.setEnd(globalRange.startContainer, endPos); }
1564
+ else {
1565
+ if (globalRange.commonAncestorContainer.textContent.trim() !== '' && !isNullOrUndefined(globalRange.commonAncestorContainer.textContent.trim()) && this.getTextRange() && this.getTextRange().lastIndexOf(this.mentionChar) !== -1) {
1566
+ range.setStart(globalRange.startContainer, globalRange.startOffset - 1);
1567
+ range.setEnd(globalRange.startContainer, globalRange.endOffset - 1);
1568
+ }
1569
+ else {
1570
+ range.setStart(globalRange.startContainer, globalRange.startOffset);
1571
+ range.setEnd(globalRange.startContainer, globalRange.endOffset);
1572
+ }
1573
+ }
1574
+ this.isTyped = false;
1575
+ range.deleteContents();
1576
+ const element: HTMLElement = this.createElement('div');
1577
+ element.innerHTML = value;
1578
+ const frag: DocumentFragment = document.createDocumentFragment();
1579
+ let node: Node;
1580
+ let lastNode: Node;
1581
+ // eslint-disable-next-line no-cond-assign
1582
+ while (node = element.firstChild) {
1583
+ lastNode = frag.appendChild(node);
1584
+ }
1585
+ range.insertNode(frag);
1586
+ if (lastNode) {
1587
+ range = range.cloneRange();
1588
+ range.setStartAfter(lastNode);
1589
+ range.collapse(true);
1590
+ selection.removeAllRanges();
1591
+ selection.addRange(range);
1592
+ }
1593
+ if (this.isPopupOpen) { this.hidePopup(); }
1594
+ this.onChangeEvent(e);
1595
+ }
1596
+ }
1597
+
1598
+ private mentionVal(value: string): string {
1599
+ const showChar: string = this.showMentionChar ? this.mentionChar : '';
1600
+ if (!isNullOrUndefined(this.displayTemplate) && !isNullOrUndefined(this.displayTempElement)) {
1601
+ value = this.displayTempElement.innerHTML;
1602
+ }
1603
+ if (this.isContentEditable(this.inputElement)) {
1604
+ if(Browser.isAndroid) {
1605
+ return '<span contenteditable="true" class="e-mention-chip">' + showChar + value + '</span>'.concat(typeof this.suffixText === 'string' ? this.suffixText : ' ');
1606
+ }
1607
+ else {
1608
+ return '<span contenteditable="false" class="e-mention-chip">' + showChar + value + '</span>'.concat(typeof this.suffixText === 'string' ? this.suffixText : ' ');
1609
+ }
1610
+ } else {
1611
+ return showChar + value;
1612
+ }
1613
+ }
1614
+
1615
+ private setDisplayTemplate(e?: KeyboardEventArgs | MouseEvent): void {
1616
+ let compiledString: Function;
1617
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
1618
+ if ((this as any).isReact) {
1619
+ this.clearTemplate(['displayTemplate']);
1620
+ if (this.displayTempElement) {
1621
+ detach(this.displayTempElement);
1622
+ this.displayTempElement = null;
1623
+ }
1624
+ }
1625
+ if (!this.displayTempElement) {
1626
+ this.displayTempElement = this.createElement('div');
1627
+ }
1628
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
1629
+ if (!(this as any).isReact) {
1630
+ this.displayTempElement.innerHTML = '';
1631
+ }
1632
+ compiledString = compile(this.displayTemplate);
1633
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
1634
+ const displayCompTemp: any = compiledString(
1635
+ this.itemData, this, 'displayTemplate', this.displayTemplateId, this.isStringTemplate, null, this.displayTempElement);
1636
+ if (displayCompTemp && displayCompTemp.length > 0) {
1637
+ append(displayCompTemp, this.displayTempElement);
1638
+ }
1639
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
1640
+ if (!(this as any).isReact) {
1641
+ this.renderTemplates();
1642
+ } else {
1643
+ this.renderTemplates(() => {
1644
+ this.updateMentionValue(e);
1645
+ });
1646
+ }
1647
+ }
1648
+
1649
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
1650
+ private renderTemplates(callBack?: any): void {
1651
+ this.renderReactTemplates(callBack);
1652
+ }
1653
+
1654
+ private setSpinnerTemplate(): void {
1655
+ let compiledString: Function;
1656
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
1657
+ if ((this as any).isReact) {
1658
+ this.clearTemplate(['spinnerTemplate']);
1659
+ if (this.spinnerTemplateElement) {
1660
+ detach(this.spinnerTemplateElement);
1661
+ this.spinnerTemplateElement = null;
1662
+ }
1663
+ }
1664
+ if (!this.spinnerTemplateElement) {
1665
+ this.spinnerTemplateElement = this.createElement('div');
1666
+ }
1667
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
1668
+ if (!(this as any).isReact) {
1669
+ this.spinnerTemplateElement.innerHTML = '';
1670
+ }
1671
+ compiledString = compile(this.spinnerTemplate);
1672
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
1673
+ const spinnerCompTemp: any = compiledString(
1674
+ null, this, 'spinnerTemplate', this.spinnerTemplateId, this.isStringTemplate, null, this.spinnerTemplateElement);
1675
+ if (spinnerCompTemp && spinnerCompTemp.length > 0) {
1676
+ for (let i: number = 0; i < spinnerCompTemp.length; i++) {
1677
+ this.spinnerTemplateElement.appendChild(spinnerCompTemp[i as number]);
1678
+ }
1679
+ }
1680
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
1681
+ if (!(this as any).isReact) {
1682
+ this.renderTemplates();
1683
+ this.popupObj.element.appendChild(this.spinnerTemplateElement);
1684
+ }
1685
+ else {
1686
+ this.renderTemplates(() => {
1687
+ this.popupObj.element.appendChild(this.spinnerTemplateElement);
1688
+ });
1689
+ }
1690
+ }
1691
+
1692
+ private onChangeEvent(eve: MouseEvent | KeyboardEvent | TouchEvent): void {
1693
+ this.isSelected = false;
1694
+ const items: FieldSettingsModel = this.detachMentionChanges(this.itemData);
1695
+ let preItems: FieldSettingsModel;
1696
+ if (typeof this.previousItemData === 'string' ||
1697
+ typeof this.previousItemData === 'boolean' ||
1698
+ typeof this.previousItemData === 'number') {
1699
+ preItems = Object.defineProperties({}, {
1700
+ value: {
1701
+ value: this.previousItemData,
1702
+ enumerable: true
1703
+ },
1704
+ text: {
1705
+ value: this.previousItemData,
1706
+ enumerable: true
1707
+ }
1708
+ });
1709
+ } else {
1710
+ preItems = this.previousItemData;
1711
+ }
1712
+ const eventArgs: MentionChangeEventArgs = {
1713
+ e: eve,
1714
+ item: this.item,
1715
+ itemData: items,
1716
+ previousItem: this.previousSelectedLI as HTMLLIElement,
1717
+ previousItemData: preItems,
1718
+ isInteracted: eve ? true : false,
1719
+ value: this.item.innerHTML,
1720
+ element: this.inputElement
1721
+ };
1722
+ this.trigger('change', eventArgs);
1723
+ }
1724
+
1725
+ private detachMentionChanges(value: string | number | boolean | {
1726
+ [key: string]: Object
1727
+ }): FieldSettingsModel {
1728
+ let items: FieldSettingsModel;
1729
+ if (typeof value === 'string' ||
1730
+ typeof value === 'boolean' ||
1731
+ typeof value === 'number') {
1732
+ items = Object.defineProperties({}, {
1733
+ value: {
1734
+ value: value,
1735
+ enumerable: true
1736
+ },
1737
+ text: {
1738
+ value: value,
1739
+ enumerable: true
1740
+ }
1741
+ });
1742
+ } else {
1743
+ items = value;
1744
+ }
1745
+ return items;
1746
+ }
1747
+
1748
+ private getItemData(): { [key: string]: string } {
1749
+ const fields: FieldSettingsModel = this.fields;
1750
+ let dataItem: { [key: string]: string | Object } | string | boolean | number = null;
1751
+ dataItem = this.itemData;
1752
+ let dataValue: string;
1753
+ let dataText: string;
1754
+ if (!isNullOrUndefined(dataItem)) {
1755
+ dataValue = getValue(fields.value, dataItem);
1756
+ dataText = getValue(fields.text, dataItem);
1757
+ }
1758
+ const value: string = <string>(!isNullOrUndefined(dataItem) &&
1759
+ !isUndefined(dataValue) ? dataValue : dataItem);
1760
+ const text: string = <string>(!isNullOrUndefined(dataItem) &&
1761
+ !isUndefined(dataValue) ? dataText : dataItem);
1762
+ return { value: value, text: text };
1763
+ }
1764
+
1765
+ private removeSelection(): void {
1766
+ if (this.list) {
1767
+ const selectedItems: Element[] = <NodeListOf<Element> &
1768
+ Element[]>this.list.querySelectorAll('.' + dropDownBaseClasses.selected);
1769
+ if (selectedItems.length) {
1770
+ removeClass(selectedItems, dropDownBaseClasses.selected);
1771
+ selectedItems[0].removeAttribute('aria-selected');
1772
+ }
1773
+ }
1774
+ }
1775
+
1776
+ private onMouseOver(e: MouseEvent): void {
1777
+ const currentLi: HTMLElement = <HTMLElement>closest(<Element>e.target, '.' + dropDownBaseClasses.li);
1778
+ this.setHover(currentLi);
1779
+ }
1780
+
1781
+ private setHover(li: HTMLElement): void {
1782
+ if (this.isValidLI(li) && !li.classList.contains(dropDownBaseClasses.hover)) {
1783
+ this.removeHover();
1784
+ addClass([li], dropDownBaseClasses.hover);
1785
+ }
1786
+ }
1787
+
1788
+ private removeHover(): void {
1789
+ if (this.list) {
1790
+ const hoveredItem: Element[] = <NodeListOf<Element> & Element[]>this.list.querySelectorAll('.' + dropDownBaseClasses.hover);
1791
+ if (hoveredItem && hoveredItem.length) {
1792
+ removeClass(hoveredItem, dropDownBaseClasses.hover);
1793
+ }
1794
+ }
1795
+ }
1796
+
1797
+ private isValidLI(li: Element | HTMLElement): boolean {
1798
+ return (li && li.hasAttribute('role') && li.getAttribute('role') === 'option');
1799
+ }
1800
+
1801
+ private onMouseLeave(): void {
1802
+ this.removeHover();
1803
+ }
1804
+
1805
+ /**
1806
+ * Search the entered text and show it in the suggestion list if available.
1807
+ *
1808
+ * @returns {void}
1809
+ */
1810
+ public search(text: string, positionX: number, positionY: number): void {
1811
+ if (this.isContentEditable(this.inputElement)) {
1812
+ this.range = this.getCurrentRange();
1813
+ }
1814
+ const currentRange: string = this.getTextRange();
1815
+ const lastWordRange: string = this.getLastLetter(currentRange);
1816
+ if ((this.ignoreCase && (text === lastWordRange || text === lastWordRange.toLowerCase()))
1817
+ || !this.ignoreCase && text === lastWordRange) {
1818
+ this.resetList(this.dataSource, this.fields);
1819
+ } else {
1820
+ if (this.isPopupOpen) {
1821
+ this.hidePopup();
1822
+ }
1823
+ return;
1824
+ }
1825
+ if (isNullOrUndefined(this.list)) {
1826
+ this.renderList();
1827
+ this.renderPopup();
1828
+ }
1829
+ else {
1830
+ this.showPopup();
1831
+ }
1832
+ this.popupObj.element.style.left = formatUnit(positionX);
1833
+ this.popupObj.element.style.top = formatUnit(positionY);
1834
+ }
1835
+
1836
+ /**
1837
+ * Removes the component from the DOM and detaches all its related event handlers. Also it removes the attributes and classes.
1838
+ *
1839
+ * @method destroy
1840
+ * @returns {void}
1841
+ */
1842
+ public destroy(): void {
1843
+ this.hidePopup();
1844
+ this.unWireEvent();
1845
+ if (this.list) {
1846
+ this.unWireListEvents();
1847
+ }
1848
+ if (this.inputElement && !this.inputElement.classList.contains('e-' + this.getModuleName())) {
1849
+ return;
1850
+ }
1851
+ this.previousSelectedLI = null;
1852
+ this.item = null;
1853
+ this.selectedLI = null;
1854
+ this.popupObj = null;
1855
+ super.destroy();
1856
+ }
1857
+
1858
+ protected getLocaleName(): string {
1859
+ return 'mention';
1860
+ }
1861
+
1862
+ protected getNgDirective(): string {
1863
+ return 'EJS-MENTION';
1864
+ }
1865
+
1866
+ /**
1867
+ * Return the module name of this component.
1868
+ *
1869
+ * @private
1870
+ * @returns {string} Return the module name of this component.
1871
+ */
1872
+ public getModuleName(): string {
1873
+ return 'mention';
1874
+ }
1875
+ }