@syncfusion/ej2-dropdowns 23.1.40-85814 → 23.1.41

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