@syncfusion/ej2-dropdowns 23.1.43 → 23.2.4-13895

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