@eturnity/eturnity_reusable_components 7.30.3-EPDM-10647.0 → 7.30.3-EPDM-8441.0

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 (322) hide show
  1. package/.prettierrc +6 -6
  2. package/.storybook/main.js +8 -8
  3. package/.storybook/preview.js +46 -46
  4. package/README.md +29 -29
  5. package/babel.config.js +5 -5
  6. package/package.json +62 -62
  7. package/postcss.config.js +6 -6
  8. package/public/index.html +17 -17
  9. package/src/App.vue +109 -109
  10. package/src/assets/icons/arrow_down.svg +3 -3
  11. package/src/assets/icons/arrow_up_red.svg +3 -3
  12. package/src/assets/icons/black_spinner.svg +35 -35
  13. package/src/assets/icons/delete_icon.svg +11 -11
  14. package/src/assets/icons/delete_icon_gray.svg +11 -11
  15. package/src/assets/icons/drag_icon.svg +8 -8
  16. package/src/assets/icons/external_icon.svg +6 -6
  17. package/src/assets/icons/language_icon.svg +6 -6
  18. package/src/assets/icons/pdf_icon.svg +6 -6
  19. package/src/assets/icons/plus_button.svg +4 -4
  20. package/src/assets/icons/search_icon_black.svg +3 -3
  21. package/src/assets/icons/subposition_icon.svg +3 -3
  22. package/src/assets/icons/subposition_marker.svg +3 -3
  23. package/src/assets/icons/warning_icon.svg +3 -3
  24. package/src/assets/svgIcons/2d_active.svg +7 -7
  25. package/src/assets/svgIcons/2d_inactive.svg +8 -8
  26. package/src/assets/svgIcons/3d_active.svg +7 -7
  27. package/src/assets/svgIcons/3d_inactive.svg +8 -8
  28. package/src/assets/svgIcons/_readme.md +7 -7
  29. package/src/assets/svgIcons/accept.svg +5 -5
  30. package/src/assets/svgIcons/activate_panels_active.svg +22 -22
  31. package/src/assets/svgIcons/activate_panels_inactive.svg +20 -20
  32. package/src/assets/svgIcons/add_icon-1.svg +3 -3
  33. package/src/assets/svgIcons/add_icon.svg +4 -4
  34. package/src/assets/svgIcons/address_book.svg +3 -3
  35. package/src/assets/svgIcons/after_sale_as_a_service.svg +6 -6
  36. package/src/assets/svgIcons/all_good.svg +3 -3
  37. package/src/assets/svgIcons/angle_active.svg +5 -5
  38. package/src/assets/svgIcons/angle_inactive.svg +4 -4
  39. package/src/assets/svgIcons/area_active.svg +11 -11
  40. package/src/assets/svgIcons/area_inactive.svg +26 -26
  41. package/src/assets/svgIcons/areas_tool.svg +14 -14
  42. package/src/assets/svgIcons/arrow_down.svg +3 -3
  43. package/src/assets/svgIcons/arrow_left.svg +4 -4
  44. package/src/assets/svgIcons/arrow_right.svg +4 -4
  45. package/src/assets/svgIcons/arrow_up.svg +3 -3
  46. package/src/assets/svgIcons/attachment.svg +3 -3
  47. package/src/assets/svgIcons/base_layer.svg +3 -3
  48. package/src/assets/svgIcons/battery.svg +3 -3
  49. package/src/assets/svgIcons/bell.svg +3 -3
  50. package/src/assets/svgIcons/bexio.svg +4 -4
  51. package/src/assets/svgIcons/bold.svg +3 -3
  52. package/src/assets/svgIcons/bom.svg +3 -3
  53. package/src/assets/svgIcons/bom_generation.svg +10 -10
  54. package/src/assets/svgIcons/bookmaker.svg +3 -3
  55. package/src/assets/svgIcons/bubble.svg +3 -3
  56. package/src/assets/svgIcons/bug.svg +5 -5
  57. package/src/assets/svgIcons/bullet_list.svg +8 -8
  58. package/src/assets/svgIcons/calendar.svg +7 -7
  59. package/src/assets/svgIcons/calendar_icon.svg +7 -7
  60. package/src/assets/svgIcons/call.svg +3 -3
  61. package/src/assets/svgIcons/camera.svg +3 -3
  62. package/src/assets/svgIcons/car.svg +3 -3
  63. package/src/assets/svgIcons/cart.svg +3 -3
  64. package/src/assets/svgIcons/charger_icon_white.svg +44 -44
  65. package/src/assets/svgIcons/checkbox.svg +3 -3
  66. package/src/assets/svgIcons/clear_formatting.svg +7 -7
  67. package/src/assets/svgIcons/clickable_info.svg +4 -4
  68. package/src/assets/svgIcons/clip.svg +3 -3
  69. package/src/assets/svgIcons/clock.svg +17 -17
  70. package/src/assets/svgIcons/clock_full.svg +3 -3
  71. package/src/assets/svgIcons/close_for_modals,_tool_tips.svg +4 -4
  72. package/src/assets/svgIcons/co_branding.svg +5 -5
  73. package/src/assets/svgIcons/collapse.svg +4 -4
  74. package/src/assets/svgIcons/collections.svg +3 -3
  75. package/src/assets/svgIcons/component_library.svg +7 -7
  76. package/src/assets/svgIcons/consumption_tariffs.svg +43 -43
  77. package/src/assets/svgIcons/context_menu-1.svg +6 -6
  78. package/src/assets/svgIcons/context_menu-2.svg +5 -5
  79. package/src/assets/svgIcons/context_menu.svg +5 -5
  80. package/src/assets/svgIcons/context_menu_tabs.svg +5 -5
  81. package/src/assets/svgIcons/cross.svg +4 -4
  82. package/src/assets/svgIcons/current_variant.svg +4 -4
  83. package/src/assets/svgIcons/dashboard.svg +3 -3
  84. package/src/assets/svgIcons/data_transfer.svg +3 -3
  85. package/src/assets/svgIcons/deadline.svg +4 -4
  86. package/src/assets/svgIcons/deal_flow.svg +5 -5
  87. package/src/assets/svgIcons/delete.svg +4 -4
  88. package/src/assets/svgIcons/delete_area_active.svg +16 -16
  89. package/src/assets/svgIcons/delete_area_inactive.svg +15 -15
  90. package/src/assets/svgIcons/direction_active-1.svg +12 -12
  91. package/src/assets/svgIcons/direction_active.svg +5 -5
  92. package/src/assets/svgIcons/direction_arrow.svg +4 -4
  93. package/src/assets/svgIcons/direction_inactive.svg +4 -4
  94. package/src/assets/svgIcons/dislike.svg +3 -3
  95. package/src/assets/svgIcons/distance_tool.svg +8 -8
  96. package/src/assets/svgIcons/distances_active.svg +9 -9
  97. package/src/assets/svgIcons/distances_inactive.svg +8 -8
  98. package/src/assets/svgIcons/distort_tool.svg +10 -10
  99. package/src/assets/svgIcons/distort_tool2.svg +16 -16
  100. package/src/assets/svgIcons/document.svg +3 -3
  101. package/src/assets/svgIcons/documents.svg +4 -4
  102. package/src/assets/svgIcons/downarrow.svg +3 -3
  103. package/src/assets/svgIcons/download.svg +4 -4
  104. package/src/assets/svgIcons/drag_icon.svg +8 -8
  105. package/src/assets/svgIcons/draggable_corner.svg +5 -5
  106. package/src/assets/svgIcons/draw_tool.svg +3 -3
  107. package/src/assets/svgIcons/duplicate-1.svg +8 -8
  108. package/src/assets/svgIcons/duplicate-2.svg +5 -5
  109. package/src/assets/svgIcons/duplicate.svg +4 -4
  110. package/src/assets/svgIcons/e-mobility_configurator.svg +6 -6
  111. package/src/assets/svgIcons/e_signature.svg +5 -5
  112. package/src/assets/svgIcons/edit_button.svg +3 -3
  113. package/src/assets/svgIcons/electricity_tariff.svg +3 -3
  114. package/src/assets/svgIcons/email.svg +3 -3
  115. package/src/assets/svgIcons/ems-1.svg +3 -3
  116. package/src/assets/svgIcons/ems.svg +3 -3
  117. package/src/assets/svgIcons/end_of_the_list.svg +5 -5
  118. package/src/assets/svgIcons/erase.svg +4 -4
  119. package/src/assets/svgIcons/external_icon.svg +5 -5
  120. package/src/assets/svgIcons/fav_icon.svg +4 -4
  121. package/src/assets/svgIcons/finance.svg +3 -3
  122. package/src/assets/svgIcons/financing_for_pv-1.svg +5 -5
  123. package/src/assets/svgIcons/financing_for_pv-2.svg +3 -3
  124. package/src/assets/svgIcons/financing_for_pv.svg +6 -6
  125. package/src/assets/svgIcons/finish-1.svg +4 -4
  126. package/src/assets/svgIcons/finish.svg +3 -3
  127. package/src/assets/svgIcons/flatten.svg +11 -11
  128. package/src/assets/svgIcons/flatten_roof.svg +20 -20
  129. package/src/assets/svgIcons/folder.svg +3 -3
  130. package/src/assets/svgIcons/free_technology.svg +5 -5
  131. package/src/assets/svgIcons/handle.svg +5 -5
  132. package/src/assets/svgIcons/heat_calc.svg +7 -7
  133. package/src/assets/svgIcons/height_equalize.svg +3 -3
  134. package/src/assets/svgIcons/height_snap.svg +3 -3
  135. package/src/assets/svgIcons/house.svg +3 -3
  136. package/src/assets/svgIcons/house_3d-1.svg +7 -7
  137. package/src/assets/svgIcons/house_3d.svg +7 -7
  138. package/src/assets/svgIcons/inclination.svg +2 -2
  139. package/src/assets/svgIcons/info.svg +3 -3
  140. package/src/assets/svgIcons/initial_situation.svg +3 -3
  141. package/src/assets/svgIcons/integrations.svg +3 -3
  142. package/src/assets/svgIcons/intro-tour-1.svg +3 -3
  143. package/src/assets/svgIcons/intro-tour.svg +3 -3
  144. package/src/assets/svgIcons/inverter-1.svg +5 -5
  145. package/src/assets/svgIcons/inverter.svg +3 -3
  146. package/src/assets/svgIcons/italic.svg +3 -3
  147. package/src/assets/svgIcons/key.svg +3 -3
  148. package/src/assets/svgIcons/layers_close.svg +4 -4
  149. package/src/assets/svgIcons/layers_open.svg +4 -4
  150. package/src/assets/svgIcons/lead_marketplace.svg +6 -6
  151. package/src/assets/svgIcons/lead_provider.svg +4 -4
  152. package/src/assets/svgIcons/length_2d.svg +2 -2
  153. package/src/assets/svgIcons/length_3d.svg +4 -4
  154. package/src/assets/svgIcons/length_calculator.svg +2 -2
  155. package/src/assets/svgIcons/length_in_2d_active.svg +12 -12
  156. package/src/assets/svgIcons/length_in_2d_inctive.svg +13 -13
  157. package/src/assets/svgIcons/light_bulb.svg +3 -3
  158. package/src/assets/svgIcons/like.svg +3 -3
  159. package/src/assets/svgIcons/line_graph.svg +3 -3
  160. package/src/assets/svgIcons/local_subsidies.svg +18 -18
  161. package/src/assets/svgIcons/location.svg +3 -3
  162. package/src/assets/svgIcons/lock.svg +3 -3
  163. package/src/assets/svgIcons/logout.svg +3 -3
  164. package/src/assets/svgIcons/loop.svg +3 -3
  165. package/src/assets/svgIcons/lunch.svg +4 -4
  166. package/src/assets/svgIcons/magic_tool.svg +6 -6
  167. package/src/assets/svgIcons/map_icon.svg +5 -5
  168. package/src/assets/svgIcons/map_settings.svg +3 -3
  169. package/src/assets/svgIcons/margin_tool.svg +4 -4
  170. package/src/assets/svgIcons/meeting.svg +6 -6
  171. package/src/assets/svgIcons/move_copy.svg +4 -4
  172. package/src/assets/svgIcons/new_area_inactive.svg +11 -11
  173. package/src/assets/svgIcons/next.svg +4 -4
  174. package/src/assets/svgIcons/not_equal_to.svg +3 -3
  175. package/src/assets/svgIcons/numbered_list.svg +6 -6
  176. package/src/assets/svgIcons/obstacle_tool.svg +9 -9
  177. package/src/assets/svgIcons/obstacle_tool_origin.svg +3 -3
  178. package/src/assets/svgIcons/offset_tool.svg +8 -8
  179. package/src/assets/svgIcons/outline_tool.svg +11 -11
  180. package/src/assets/svgIcons/pan_tool.svg +12 -12
  181. package/src/assets/svgIcons/panels_tool.svg +8 -8
  182. package/src/assets/svgIcons/pen_tool.svg +4 -4
  183. package/src/assets/svgIcons/picker_tool.svg +4 -4
  184. package/src/assets/svgIcons/picture.svg +3 -3
  185. package/src/assets/svgIcons/pin.svg +5 -5
  186. package/src/assets/svgIcons/presentation.svg +3 -3
  187. package/src/assets/svgIcons/previous.svg +4 -4
  188. package/src/assets/svgIcons/profile-1.svg +4 -4
  189. package/src/assets/svgIcons/profile.svg +4 -4
  190. package/src/assets/svgIcons/profitability.svg +3 -3
  191. package/src/assets/svgIcons/project_analysis.svg +4 -4
  192. package/src/assets/svgIcons/project_settings.svg +4 -4
  193. package/src/assets/svgIcons/pv.svg +3 -3
  194. package/src/assets/svgIcons/quotations.svg +6 -6
  195. package/src/assets/svgIcons/redo.svg +6 -6
  196. package/src/assets/svgIcons/resizer.svg +5 -5
  197. package/src/assets/svgIcons/roof_layer.svg +3 -3
  198. package/src/assets/svgIcons/rotate_tool.svg +3 -3
  199. package/src/assets/svgIcons/rotate_view.svg +5 -5
  200. package/src/assets/svgIcons/ruler_tool.svg +3 -3
  201. package/src/assets/svgIcons/run_simulation.svg +3 -3
  202. package/src/assets/svgIcons/save.svg +3 -3
  203. package/src/assets/svgIcons/scaling_tool.svg +8 -8
  204. package/src/assets/svgIcons/search.svg +3 -3
  205. package/src/assets/svgIcons/security.svg +3 -3
  206. package/src/assets/svgIcons/settings.svg +3 -3
  207. package/src/assets/svgIcons/show_in_a_new_tab.svg +12 -12
  208. package/src/assets/svgIcons/smartphone.svg +4 -4
  209. package/src/assets/svgIcons/solar_calc.svg +13 -13
  210. package/src/assets/svgIcons/sorting.svg +4 -4
  211. package/src/assets/svgIcons/split.svg +12 -12
  212. package/src/assets/svgIcons/start_of_the_list.svg +5 -5
  213. package/src/assets/svgIcons/strikethrough.svg +4 -4
  214. package/src/assets/svgIcons/subscriptions.svg +3 -3
  215. package/src/assets/svgIcons/subsidies-1.svg +5 -5
  216. package/src/assets/svgIcons/subsidies-2.svg +3 -3
  217. package/src/assets/svgIcons/subsidies.svg +3 -3
  218. package/src/assets/svgIcons/subtract_icon.svg +3 -3
  219. package/src/assets/svgIcons/suitcase.svg +3 -3
  220. package/src/assets/svgIcons/summer.svg +3 -3
  221. package/src/assets/svgIcons/template_icon_not_clickable.svg +6 -6
  222. package/src/assets/svgIcons/transfer.svg +4 -4
  223. package/src/assets/svgIcons/trim_tool.svg +4 -4
  224. package/src/assets/svgIcons/truck.svg +3 -3
  225. package/src/assets/svgIcons/underlined.svg +3 -3
  226. package/src/assets/svgIcons/undo.svg +6 -6
  227. package/src/assets/svgIcons/uparrow.svg +3 -3
  228. package/src/assets/svgIcons/update.svg +3 -3
  229. package/src/assets/svgIcons/upload_avatar-1.svg +12 -12
  230. package/src/assets/svgIcons/upload_avatar.svg +5 -5
  231. package/src/assets/svgIcons/upload_image.svg +8 -8
  232. package/src/assets/svgIcons/upload_image_tool.svg +7 -7
  233. package/src/assets/svgIcons/variants.svg +6 -6
  234. package/src/assets/svgIcons/vertical_tool.svg +3 -3
  235. package/src/assets/svgIcons/virtual_storage.svg +4 -4
  236. package/src/assets/svgIcons/warning.svg +4 -4
  237. package/src/assets/svgIcons/way.svg +5 -5
  238. package/src/assets/svgIcons/wifi.svg +3 -3
  239. package/src/assets/svgIcons/winter.svg +3 -3
  240. package/src/assets/svgIcons/workflow_template.svg +11 -11
  241. package/src/assets/theme.js +41 -41
  242. package/src/components/addNewButton/AddNewButton.stories.js +24 -24
  243. package/src/components/addNewButton/index.vue +64 -64
  244. package/src/components/banner/actionBanner/index.vue +65 -65
  245. package/src/components/banner/banner/banner.stories.js +31 -31
  246. package/src/components/banner/banner/index.vue +188 -188
  247. package/src/components/banner/infoBanner/index.vue +69 -69
  248. package/src/components/buttons/buttonIcon/index.vue +142 -142
  249. package/src/components/buttons/closeButton/CloseButton.stories.js +29 -29
  250. package/src/components/buttons/closeButton/index.vue +61 -61
  251. package/src/components/buttons/mainButton/index.vue +140 -140
  252. package/src/components/card/index.vue +96 -96
  253. package/src/components/collapsableInfoText/index.vue +125 -125
  254. package/src/components/deleteIcon/DeleteIcon.stories.js +29 -29
  255. package/src/components/deleteIcon/index.vue +78 -78
  256. package/src/components/draggableInputHandle/index.vue +46 -46
  257. package/src/components/dropdown/Dropdown.stories.js +54 -54
  258. package/src/components/dropdown/index.vue +138 -138
  259. package/src/components/errorMessage/index.vue +64 -64
  260. package/src/components/filter/filterSettings.vue +633 -633
  261. package/src/components/filter/index.vue +154 -154
  262. package/src/components/filter/parentDropdown.vue +91 -91
  263. package/src/components/icon/Icons.stories.js +41 -41
  264. package/src/components/icon/iconCache.js +23 -23
  265. package/src/components/icon/iconCollection.vue +68 -68
  266. package/src/components/icon/index.vue +140 -140
  267. package/src/components/iconWrapper/index.vue +179 -179
  268. package/src/components/infoCard/index.vue +42 -42
  269. package/src/components/infoText/index.vue +165 -165
  270. package/src/components/inputs/checkbox/Checkbox.stories.js +57 -57
  271. package/src/components/inputs/checkbox/index.vue +214 -214
  272. package/src/components/inputs/inputNumber/InputNumber.stories.js +150 -150
  273. package/src/components/inputs/inputNumber/index.vue +784 -789
  274. package/src/components/inputs/inputNumberQuestion/index.vue +215 -215
  275. package/src/components/inputs/inputText/InputText.stories.js +75 -75
  276. package/src/components/inputs/inputText/index.vue +375 -375
  277. package/src/components/inputs/radioButton/RadioButton.stories.js +58 -58
  278. package/src/components/inputs/radioButton/index.vue +270 -270
  279. package/src/components/inputs/searchInput/SearchInput.stories.js +40 -40
  280. package/src/components/inputs/searchInput/index.vue +150 -150
  281. package/src/components/inputs/select/index.vue +1028 -907
  282. package/src/components/inputs/select/option/index.vue +139 -148
  283. package/src/components/inputs/select/select.stories.js +59 -59
  284. package/src/components/inputs/slider/index.vue +126 -126
  285. package/src/components/inputs/switchField/index.vue +256 -256
  286. package/src/components/inputs/textAreaInput/TextAreaInput.stories.js +127 -127
  287. package/src/components/inputs/textAreaInput/index.vue +200 -200
  288. package/src/components/inputs/toggle/Toggle.stories.js +77 -77
  289. package/src/components/inputs/toggle/index.vue +288 -288
  290. package/src/components/label/index.vue +99 -99
  291. package/src/components/markerItem/index.vue +86 -86
  292. package/src/components/modals/actionModal/index.vue +64 -64
  293. package/src/components/modals/infoModal/index.vue +49 -49
  294. package/src/components/modals/modal/index.vue +188 -188
  295. package/src/components/modals/modal/modal.stories.js +31 -31
  296. package/src/components/navigationTabs/index.vue +112 -112
  297. package/src/components/pageSubtitle/index.vue +61 -61
  298. package/src/components/pageTitle/index.vue +68 -68
  299. package/src/components/pagination/index.vue +145 -145
  300. package/src/components/progressBar/index.vue +125 -125
  301. package/src/components/projectMarker/index.vue +298 -298
  302. package/src/components/rangeSlider/Slider.vue +547 -547
  303. package/src/components/rangeSlider/index.vue +517 -517
  304. package/src/components/rangeSlider/utils/dom.js +49 -49
  305. package/src/components/rangeSlider/utils/fns.js +26 -26
  306. package/src/components/selectedOptions/index.vue +145 -145
  307. package/src/components/sideMenu/index.vue +270 -270
  308. package/src/components/spinner/index.vue +68 -68
  309. package/src/components/tableDropdown/index.vue +638 -638
  310. package/src/components/tables/mainTable/exampleNested.vue +328 -328
  311. package/src/components/tables/mainTable/index.vue +493 -493
  312. package/src/components/tables/viewTable/index.vue +195 -195
  313. package/src/components/tabsHeader/index.vue +83 -83
  314. package/src/components/threeDots/index.vue +407 -407
  315. package/src/components/videoThumbnail/index.vue +103 -103
  316. package/src/components/videoThumbnail/videoThumbnail.stories.js +35 -35
  317. package/src/helpers/currencyMapping.js +28 -28
  318. package/src/helpers/numberConverter.js +103 -103
  319. package/src/helpers/translateLang.js +127 -127
  320. package/src/main.js +6 -6
  321. package/src/mixins/inputValidations.js +97 -97
  322. package/src/utils/index.js +0 -12
@@ -1,907 +1,1028 @@
1
- <template>
2
- <Container
3
- :selectWidth="selectWidth"
4
- :noRelative="noRelative"
5
- @mouseenter="mouseEnterHandler"
6
- @mouseleave="mouseLeaveHandler"
7
- >
8
- <input-wrapper
9
- :hasLabel="!!label && label.length > 0"
10
- :alignItems="alignItems"
11
- :noRelative="noRelative"
12
- >
13
- <label-wrapper
14
- v-if="label"
15
- :data-id="labelDataId"
16
- :infoTextMessage="!!infoTextMessage"
17
- >
18
- <input-label
19
- :fontColor="
20
- labelFontColor || colorMode == 'dark' ? 'white' : 'eturnityGrey'
21
- "
22
- :fontSize="fontSize"
23
- >{{ label }}
24
- <optionalLabel v-if="labelOptional">
25
- ({{ $gettext('Optional') }})</optionalLabel
26
- >
27
- </input-label>
28
- <info-text
29
- v-if="infoTextMessage || !!this.$slots.infoText"
30
- :text="infoTextMessage"
31
- :size="infoTextSize"
32
- :alignArrow="infoTextAlignArrow"
33
- :width="infoTextWidth"
34
- :max-width="infoTextWidth"
35
- >
36
- <slot name="infoText" />
37
- </info-text>
38
- </label-wrapper>
39
- <select-button-wrapper :disabled="disabled">
40
- <selectButton
41
- ref="select"
42
- class="select-button"
43
- @click="toggleDropdown"
44
- :selectWidth="selectWidth"
45
- :selectHeight="selectHeight"
46
- :height="height"
47
- :selectMinHeight="selectMinHeight"
48
- :bgColor="
49
- buttonBgColor || colorMode == 'dark' ? 'transparentBlack1' : 'white'
50
- "
51
- :fontColor="
52
- buttonFontColor || colorMode == 'dark' ? 'white' : 'black'
53
- "
54
- :fontSize="fontSize"
55
- :hasError="hasError"
56
- :hasNoPadding="isSearchBarVisible || !hasSelectButtonPadding"
57
- :disabled="disabled"
58
- @keydown="onKeyDown"
59
- :showBorder="showBorder"
60
- :data-id="dataId"
61
- :paddingLeft="paddingLeft"
62
- :tablePaddingLeft="tablePaddingLeft"
63
- :noRelative="noRelative"
64
- :showDisabledBackground="showDisabledBackground"
65
- >
66
- <draggableInputHandle
67
- v-if="isDraggable && !isSearchBarVisible"
68
- :height="selectHeight"
69
- />
70
- <inputText
71
- v-if="isSearchBarVisible"
72
- ref="searchInput"
73
- tabindex="0"
74
- inputHeight="34px"
75
- :noBorder="true"
76
- :fontSize="fontSize"
77
- backgroundColor="transparent"
78
- :fontColor="
79
- buttonFontColor || colorMode == 'dark' ? 'white' : 'black'
80
- "
81
- :value="textSearch"
82
- :inputWidth="computedWidth"
83
- @keydown.stop="onKeyDown"
84
- @input-change="searchChange"
85
- @click.stop
86
- />
87
- <selector
88
- v-else
89
- :showBorder="showBorder"
90
- :selectWidth="selectWidth"
91
- :paddingLeft="paddingLeft"
92
- >
93
- <slot name="selector" :selectedValue="selectedValue"></slot>
94
- </selector>
95
- <Caret @click.stop="toggleCaretDropdown" class="caret_dropdown">
96
- <icon
97
- v-if="isDropdownOpen"
98
- name="arrow_up"
99
- size="12px"
100
- :color="
101
- caretColor || colorMode == 'dark'
102
- ? 'white'
103
- : 'transparentBlack1'
104
- "
105
- />
106
- <icon
107
- v-else
108
- name="arrow_down"
109
- size="12px"
110
- :color="
111
- caretColor || colorMode == 'dark'
112
- ? 'white'
113
- : 'transparentBlack1'
114
- "
115
- />
116
- </Caret>
117
- </selectButton>
118
- <DropdownWrapper ref="dropdownWrapperRef" :noRelative="noRelative">
119
- <Teleport to="#portal-target">
120
- <selectDropdown
121
- ref="dropdown"
122
- v-show="isSelectDropdownShown"
123
- :dropdownPosition="dropdownPosition"
124
- :hoveredIndex="hoveredIndex"
125
- :hoveredValue="hoveredValue"
126
- :isActive="isActive"
127
- :optionWidth="getOptionWidth"
128
- :hoveredBgColor="
129
- colorMode == 'dark' ? '#000000' : dropdownBgColor
130
- "
131
- :bgColor="
132
- dropdownBgColor || colorMode == 'dark' ? 'black' : 'white'
133
- "
134
- :fontColor="
135
- dropdownFontColor || colorMode == 'dark' ? 'white' : 'black'
136
- "
137
- :noRelative="noRelative"
138
- :fontSize="fontSize"
139
- :minWidth="minWidth"
140
- :selectedValue="selectedValue"
141
- @option-selected="optionSelected"
142
- @option-hovered="optionHovered"
143
- @mouseleave="optionLeave"
144
- >
145
- <slot name="dropdown"></slot>
146
- </selectDropdown>
147
- </Teleport>
148
- </DropdownWrapper>
149
- </select-button-wrapper>
150
- </input-wrapper>
151
- </Container>
152
- </template>
153
-
154
- <script>
155
- //How to use it
156
- // <Select
157
- // hoverDropdown="true"
158
- // selectWidth="100%"
159
- // minWidth="220px"
160
- // optionWidth="50%"
161
- // label="that is a label"
162
- // alignItems="vertical"
163
- // label-data-id="test-label-data-id"
164
- // data-id="test-data-id"
165
- // :hasSelectButtonPadding="false"
166
- // >
167
- // <template #selector="{selectedValue}">
168
- // value selected: {{selectedValue}}
169
- // </template>
170
- // <template #dropdown>
171
- // <Option value="1">value one</Option>
172
- // <Option value="2">value two</Option>
173
- // <Option value="3">value three</Option>
174
- // <Option value="4">value four</Option>
175
- // </template>
176
- // </Select>
177
-
178
- import { Teleport } from 'vue'
179
- import styled from 'vue3-styled-components'
180
- import InfoText from '../../infoText'
181
- import icon from '../../icon'
182
- import inputText from '../inputText'
183
- import draggableInputHandle from '../../draggableInputHandle'
184
- import { debounce } from '../../../utils'
185
-
186
- const CARET_WIDTH = '30px'
187
- const BORDER_WIDTH = '1px'
188
-
189
- const Caret = styled.div`
190
- display: flex;
191
- align-items: center;
192
- justify-content: center;
193
- width: ${CARET_WIDTH};
194
- min-width: ${CARET_WIDTH};
195
- height: 100%;
196
- align-items: center;
197
- cursor: pointer;
198
- margin-left: auto;
199
- `
200
-
201
- const selectorProps = {
202
- selectWidth: String,
203
- paddingLeft: String,
204
- showBorder: Boolean
205
- }
206
- const Selector = styled('div', selectorProps)`
207
- ${(props) =>
208
- props.selectWidth === '100%'
209
- ? 'width: 100%;'
210
- : `width: calc(${props.selectWidth} -
211
- (
212
- ${CARET_WIDTH} +
213
- ${props.paddingLeft}
214
- ${props.showBorder ? `+ (${BORDER_WIDTH} * 2)` : ''}
215
- )
216
- );
217
- white-space: nowrap;
218
- text-overflow: ellipsis;
219
- overflow: hidden;`}
220
- `
221
-
222
- const labelAttrs = { fontSize: String, fontColor: String }
223
- const InputLabel = styled('div', labelAttrs)`
224
- color: ${(props) =>
225
- props.theme.colors[props.fontColor]
226
- ? props.theme.colors[props.fontColor]
227
- : props.fontColor};
228
- font-size: ${(props) => props.fontSize};
229
- font-weight: 700;
230
- `
231
- const optionalLabel = styled.span`
232
- font-weight: 300;
233
- `
234
- const inputProps = {
235
- selectWidth: String,
236
- optionWidth: String,
237
- noRelative: Boolean
238
- }
239
- const Container = styled('div', inputProps)`
240
- width: ${(props) => props.selectWidth};
241
- position: ${(props) => (props.noRelative ? 'static' : 'relative')};
242
- display: inline-block;
243
- `
244
-
245
- const LabelWrapperAttrs = { infoTextMessage: Boolean }
246
- const LabelWrapper = styled('div', LabelWrapperAttrs)`
247
- display: inline-grid;
248
- grid-template-columns: ${(props) =>
249
- props.infoTextMessage ? 'auto auto' : 'auto'};
250
- grid-gap: 12px;
251
- align-items: center;
252
- justify-content: start;
253
- `
254
-
255
- const SelectButtonWrapperAttrs = {
256
- disabled: Boolean
257
- }
258
- const SelectButtonWrapper = styled('div', SelectButtonWrapperAttrs)`
259
- ${(props) => (props.disabled ? 'cursor: not-allowed' : 'cursor: pointer')};
260
- `
261
-
262
- const selectButtonAttrs = {
263
- bgColor: String,
264
- fontColor: String,
265
- hasError: Boolean,
266
- disabled: Boolean,
267
- selectHeight: String,
268
- selectWidth: String,
269
- height: String,
270
- selectMinHeight: String,
271
- hasNoPadding: Boolean,
272
- showBorder: Boolean,
273
- paddingLeft: String,
274
- noRelative: Boolean,
275
- tablePaddingLeft: String,
276
- showDisabledBackground: Boolean
277
- }
278
- const selectButton = styled('div', selectButtonAttrs)`
279
- position: ${(props) => (props.noRelative ? 'static' : 'relative')};
280
- box-sizing: border-box;
281
- border-radius: 4px;
282
- max-width: ${(props) => (props.selectWidth ? props.selectWidth : '100%')};
283
- ${(props) =>
284
- props.isSearchBarVisible
285
- ? ''
286
- : `padding-left: ${
287
- props.hasNoPadding
288
- ? '0'
289
- : props.tablePaddingLeft
290
- ? props.tablePaddingLeft
291
- : props.paddingLeft
292
- }`};
293
- text-align: left;
294
- min-height: ${(props) =>
295
- props.selectHeight
296
- ? props.selectHeight
297
- : props.selectMinHeight
298
- ? props.selectMinHeight
299
- : props.height
300
- ? props.height
301
- : '36px'};
302
- display: flex;
303
- align-items: center;
304
- height: ${(props) => props.selectHeight};
305
- ${({ showBorder, theme, hasError }) =>
306
- showBorder &&
307
- `
308
- border: ${BORDER_WIDTH} solid ${
309
- hasError ? theme.colors.red : theme.colors.grey4
310
- }
311
- `}
312
- background-color:${(props) =>
313
- props.disabled && props.showDisabledBackground
314
- ? props.theme.colors.grey5
315
- : props.theme.colors[props.bgColor]
316
- ? props.theme.colors[props.bgColor]
317
- : props.bgColor};
318
- color: ${(props) =>
319
- props.theme.colors[props.fontColor]
320
- ? props.theme.colors[props.fontColor]
321
- : props.fontColor};
322
- ${(props) => (props.disabled ? 'pointer-events: none' : '')};
323
- overflow: hidden;
324
- & > .handle {
325
- border-right: ${(props) =>
326
- props.hasError ? props.theme.colors.red : props.theme.colors.grey4}
327
- 1px solid;
328
- }
329
- `
330
- const selectDropdownAttrs = {
331
- hoveredBgColor: String,
332
- bgColor: String,
333
- fontColor: String,
334
- optionWidth: String,
335
- hoveredIndex: Number,
336
- fontSize: String,
337
- dropdownPosition: Object,
338
- hoveredValue: Number | String,
339
- selectedValue: Number | String,
340
- noRelative: Boolean,
341
- minWidth: String
342
- }
343
- const selectDropdown = styled('div', selectDropdownAttrs)`
344
- box-sizing: border-box;
345
- z-index: ${(props) => (props.isActive ? '2' : '99999')};
346
- position: absolute;
347
- top: ${(props) =>
348
- props.noRelative ? 'auto' : props.dropdownPosition?.top + 'px'};
349
- left: ${(props) => props.dropdownPosition?.left}px;
350
- border: ${BORDER_WIDTH} solid ${(props) => props.theme.colors.grey4};
351
- border-radius: 4px;
352
- display: flex;
353
- flex-direction: column;
354
- align-items: flex-start;
355
- padding: 0px;
356
- box-shadow: 0px 8px 16px 0px rgba(0, 0, 0, 0.2);
357
- width: ${(props) => (props.optionWidth ? props.optionWidth : '100%')};
358
- min-width: ${(props) =>
359
- props.minWidth
360
- ? props.minWidth
361
- : props.optionWidth
362
- ? props.optionWidth
363
- : '100%'};
364
- background-color: ${(props) =>
365
- props.theme.colors[props.bgColor]
366
- ? props.theme.colors[props.bgColor]
367
- : props.bgColor};
368
- color: ${(props) =>
369
- props.theme.colors[props.fontColor]
370
- ? props.theme.colors[props.fontColor]
371
- : props.fontColor};
372
- max-height: 300px;
373
- overflow-y: auto;
374
- & > div[data-value='${(props) => props.hoveredValue}'] {
375
- background-color: ${(props) =>
376
- props.theme.colors[props.hoveredBgColor]
377
- ? props.theme.colors[props.hoveredBgColor]
378
- : props.hoveredBgColor};
379
- }
380
- font-size: ${(props) => props.fontSize};
381
- `
382
- selectDropdown.emits = ['option-hovered', 'option-selected']
383
- const DropdownAttrs = { noRelative: Boolean }
384
- const DropdownWrapper = styled('div', DropdownAttrs)`
385
- position: ${(props) => (props.noRelative ? 'static' : 'relative')};
386
- `
387
- const inputAttrs = {
388
- alignItems: String,
389
- hasLabel: Boolean,
390
- noRelative: Boolean
391
- }
392
- const InputWrapper = styled('div', inputAttrs)`
393
- position: ${(props) => (props.noRelative ? 'static' : 'relative')};
394
- display: grid;
395
- width: 100%;
396
- min-width: ${(props) => (props.minWidth ? props.minWidth : '150px')};
397
- align-items: center;
398
- gap: 8px;
399
- grid-template-columns: ${(props) =>
400
- props.alignItems === 'vertical' || !props.hasLabel ? '1fr' : 'auto 1fr'};
401
- `
402
-
403
- const DROPDOWN_HEIGHT_OFFSET = 4
404
- const DROPDOWN_TOP_OFFSET = 21
405
- const MIN_OPTION_LENGTH = 5
406
-
407
- const DROPDOWN_MENU_POSITIONS = {
408
- Automatic: 'automatic',
409
- Bottom: 'bottom'
410
- }
411
-
412
- export default {
413
- name: 'RCselect',
414
-
415
- props: {
416
- value: {
417
- required: false,
418
- default: null
419
- },
420
- fontSize: {
421
- required: false,
422
- default: '13px'
423
- },
424
- noRelative: {
425
- required: false,
426
- default: false
427
- },
428
- label: {
429
- required: false
430
- },
431
- labelOptional: {
432
- required: false,
433
- default: false
434
- },
435
- labelDataId: {
436
- required: false,
437
- default: ''
438
- },
439
- infoTextMessage: {
440
- required: false
441
- },
442
- selectWidth: {
443
- type: String,
444
- required: false,
445
- default: '100%'
446
- },
447
- minWidth: {
448
- required: false
449
- },
450
- maxWidth: {
451
- required: false
452
- },
453
- selectHeight: {
454
- type: String,
455
- required: false,
456
- default: null
457
- },
458
- height: {
459
- required: false,
460
- default: null
461
- },
462
- selectMinHeight: {
463
- required: false,
464
- default: '36px'
465
- },
466
- optionWidth: {
467
- required: false,
468
- default: null
469
- },
470
- hoverDropdown: {
471
- required: false,
472
- default: false
473
- },
474
- dropdownAutoClose: {
475
- required: false,
476
- default: false
477
- },
478
- alignItems: {
479
- required: false,
480
- default: 'horizontal'
481
- },
482
- buttonBgColor: {
483
- required: false
484
- },
485
- buttonFontColor: {
486
- required: false
487
- },
488
- dropdownBgColor: {
489
- required: false,
490
- default: 'grey5'
491
- },
492
- dropdownFontColor: {
493
- required: false
494
- },
495
- dropDownArrowVisible: {
496
- required: false,
497
- default: true
498
- },
499
- caretColor: {
500
- required: false
501
- },
502
- labelFontColor: {
503
- required: false
504
- },
505
- colorMode: {
506
- required: false,
507
- default: 'light'
508
- },
509
- isSearchable: {
510
- required: false,
511
- default: true
512
- },
513
- hasError: {
514
- required: false,
515
- default: false
516
- },
517
- disabled: {
518
- required: false,
519
- default: false
520
- },
521
- isAutoSearch: {
522
- required: false,
523
- default: true
524
- },
525
- showBorder: {
526
- required: false,
527
- default: true
528
- },
529
- infoTextSize: {
530
- required: false,
531
- default: '14px'
532
- },
533
- dataId: {
534
- type: String,
535
- default: ''
536
- },
537
- hasSelectButtonPadding: {
538
- required: false,
539
- type: Boolean,
540
- default: true
541
- },
542
- isDraggable: {
543
- type: Boolean,
544
- default: false
545
- },
546
- leftPadding: {
547
- type: String,
548
- default: '15px'
549
- },
550
- tablePaddingLeft: {
551
- required: false
552
- },
553
- showDisabledBackground: {
554
- required: false,
555
- default: true
556
- },
557
- minOptionLength: {
558
- type: Number,
559
- default: MIN_OPTION_LENGTH
560
- },
561
- dropdownMenuPosition: {
562
- type: String,
563
- default: DROPDOWN_MENU_POSITIONS.Automatic // options: ['automatic', bottom]
564
- },
565
- infoTextWidth: {
566
- type: String,
567
- required: false
568
- },
569
- infoTextAlignArrow: {
570
- type: String,
571
- required: false
572
- }
573
- },
574
-
575
- components: {
576
- selectButton,
577
- SelectButtonWrapper,
578
- selectDropdown,
579
- Container,
580
- InputLabel,
581
- LabelWrapper,
582
- optionalLabel,
583
- InfoText,
584
- InputWrapper,
585
- DropdownWrapper,
586
- icon,
587
- Caret,
588
- Selector,
589
- inputText,
590
- Teleport,
591
- draggableInputHandle
592
- },
593
-
594
- data() {
595
- return {
596
- selectedValue: null,
597
- paddingLeft: this.isDraggable ? '30px' : this.leftPadding,
598
- isDropdownOpen: false,
599
- isActive: false,
600
- textSearch: '',
601
- hoveredIndex: 0,
602
- isClickOutsideActive: false,
603
- dropdownPosition: {
604
- left: null,
605
- top: null
606
- },
607
- dropdownWidth: null,
608
- hoveredValue: null
609
- }
610
- },
611
- mounted() {
612
- this.observeDropdownHeight()
613
- this.observeSelectWidth()
614
- window.addEventListener('resize', this.handleSetDropdownOffet)
615
- },
616
- beforeMount() {
617
- this.selectedValue = this.value
618
- document.addEventListener('click', this.clickOutside)
619
- this.getDropdownPosition()
620
- window.removeEventListener('resize', this.handleSetDropdownOffet)
621
- if (this.dropdownResizeObserver) this.dropdownResizeObserver.disconnect()
622
- if (this.selectResizeObserver) this.selectResizeObserver.disconnect()
623
- },
624
- unmounted() {
625
- document.removeEventListener('click', this.clickOutside)
626
- },
627
- methods: {
628
- focus() {
629
- this.isActive = true
630
- },
631
- blur(e) {
632
- this.isActive = false
633
- this.$emit('blur', e)
634
- },
635
- toggleDropdown() {
636
- this.isDropdownOpen = !this.isDropdownOpen
637
- },
638
- toggleCaretDropdown() {
639
- this.isDropdownOpen = !this.isDropdownOpen
640
- },
641
- closeDropdown() {
642
- this.blur()
643
- this.clearSearch()
644
- this.isDropdownOpen = false
645
- },
646
- clearSearch() {
647
- this.textSearch = ''
648
- this.searchChange('')
649
- },
650
- optionSelected(e) {
651
- this.selectedValue = e
652
- this.closeDropdown()
653
- this.blur()
654
- this.$emit('input-change', e)
655
- },
656
- optionHovered: debounce(function (e) {
657
- this.hoveredValue = e
658
- }, 300),
659
- mouseEnterHandler() {
660
- if (this.hoverDropdown) {
661
- this.focus()
662
- this.isDropdownOpen = true
663
- }
664
- },
665
- mouseLeaveHandler() {
666
- if (this.hoverDropdown) {
667
- this.blur()
668
- }
669
- },
670
- optionLeave() {
671
- if (this.dropdownAutoClose) {
672
- this.isDropdownOpen = false
673
- }
674
- },
675
- searchChange(value) {
676
- this.textSearch = value
677
- this.$emit('search-change', value)
678
- const dropdownChildren = [...this.$refs.dropdown.$el.children]
679
- dropdownChildren.forEach((el) => {
680
- if (!el.textContent.toLowerCase().includes(value.toLowerCase())) {
681
- el.style.display = 'none'
682
-
683
- return
684
- }
685
-
686
- el.style.display = 'inherit'
687
- })
688
- },
689
- clickOutside(event) {
690
- const dropdownRef = this.$refs.dropdown
691
- // we need to prevent closing on selecting an option, because in the case of
692
- // a disabled option, we don't want to close the dropdown
693
- if (!this.isClickOutsideActive) return
694
- if (
695
- this.$refs.select.$el == event.target ||
696
- this.$refs.select.$el.contains(event.target) ||
697
- event.target.id === 'more-button' ||
698
- event.target.parentNode === dropdownRef.$el
699
- ) {
700
- return
701
- } else {
702
- this.closeDropdown()
703
- }
704
- },
705
- onKeyDown(e) {
706
- if (e.key == 'ArrowDown') {
707
- this.onArrowPress(1)
708
- } else if (e.key == 'ArrowUp') {
709
- this.onArrowPress(-1)
710
- } else if (e.key == 'Enter') {
711
- const optionHoveredComponent = [...this.$refs.dropdown.$el.children][
712
- (this.hoveredIndex - 1 + this.optionLength) % this.optionLength
713
- ]
714
- this.optionSelected(optionHoveredComponent.$el.dataset.value)
715
- }
716
- },
717
- // If some part of the dropdown menu is outside viewport of the bottom of the screen,
718
- // we need to offset it and display it at the top of the select dropdown instead
719
- async getDropdownPosition() {
720
- if (
721
- !this.$refs.dropdownWrapperRef ||
722
- !this.$refs.select ||
723
- !this.$refs.dropdown
724
- ) {
725
- return
726
- }
727
- await this.$nextTick()
728
- const isDisplayedAtBottom = await this.generateDropdownPosition()
729
- // If the dropdown menu is going to be displayed at the bottom,
730
- // we need reverify its position after a dom update (nextTick)
731
- await this.$nextTick()
732
- if (isDisplayedAtBottom) this.generateDropdownPosition()
733
- },
734
- async generateDropdownPosition() {
735
- const isDropdownNotCompletelyVisible =
736
- await this.isBottomOfDropdownOutOfViewport()
737
- const dropdownWrapperEl = this.$refs.dropdownWrapperRef.$el
738
- const selectButtonHeight = this.$refs.select.$el.clientHeight
739
- const dropdownHeight = this.$refs.dropdown.$el.clientHeight
740
- const dropdownWrapperRelativeHeight =
741
- dropdownWrapperEl.getBoundingClientRect().top +
742
- window.scrollY +
743
- DROPDOWN_HEIGHT_OFFSET
744
-
745
- const top =
746
- isDropdownNotCompletelyVisible ||
747
- (!isDropdownNotCompletelyVisible &&
748
- this.dropdownMenuPosition === DROPDOWN_MENU_POSITIONS.Bottom)
749
- ? dropdownWrapperRelativeHeight
750
- : dropdownWrapperRelativeHeight -
751
- dropdownHeight -
752
- selectButtonHeight -
753
- DROPDOWN_TOP_OFFSET
754
- const left = this.dropdownPosition.left
755
- ? this.dropdownPosition.left
756
- : dropdownWrapperEl.getBoundingClientRect().left + window.scrollX
757
-
758
- this.dropdownPosition = { left: Math.floor(left), top: Math.floor(top) }
759
-
760
- return isDropdownNotCompletelyVisible
761
- },
762
- async isBottomOfDropdownOutOfViewport() {
763
- if (
764
- !this.$refs.dropdown ||
765
- this.dropdownMenuPosition === DROPDOWN_MENU_POSITIONS.Bottom
766
- ) {
767
- return false
768
- }
769
-
770
- await this.$nextTick()
771
- const rect = this.$refs.dropdown.$el.getBoundingClientRect()
772
- const windowHeight =
773
- window.innerHeight || document.documentElement.clientHeight
774
-
775
- if (windowHeight <= 650) return true
776
-
777
- // using Math.floor because the offsets may contain decimals we are not going to consider here
778
- return Math.floor(rect.top) + Math.floor(rect.height) <= windowHeight
779
- },
780
- observeDropdownHeight() {
781
- if (!this.$refs.dropdown) return
782
- this.dropdownResizeObserver = new ResizeObserver(() => {
783
- this.$nextTick(() => this.getDropdownPosition())
784
- })
785
- this.dropdownResizeObserver.observe(this.$refs.dropdown.$el)
786
- },
787
- handleSetDropdownOffet() {
788
- if (!this.$refs.select) return
789
- this.dropdownPosition.left = Math.floor(
790
- this.$refs.select.$el.getBoundingClientRect().left
791
- )
792
- this.getDropdownWidth()
793
- },
794
- observeSelectWidth() {
795
- if (!this.$refs.select) return
796
- this.selectResizeObserver = new ResizeObserver(() =>
797
- // eslint-disable-next-line vue/valid-next-tick
798
- this.$nextTick(() => this.getDropdownWidth())
799
- )
800
- this.selectResizeObserver.observe(this.$refs.dropdown.$el)
801
- },
802
- async getDropdownWidth() {
803
- if (!this.$refs.select) return
804
- await this.$nextTick()
805
- this.dropdownWidth = `${this.$refs.select.$el.clientWidth}px`
806
- },
807
- onArrowPress(dir) {
808
- let newHoveredElem
809
- const currentHoveredElem = this.$refs.dropdown.$el.querySelector(
810
- `[data-value="${this.hoveredValue}"]`
811
- )
812
- if (currentHoveredElem) {
813
- if (dir > 0) {
814
- newHoveredElem = currentHoveredElem.nextElementSibling
815
- } else {
816
- newHoveredElem = currentHoveredElem.previousElementSibling
817
- }
818
- if (newHoveredElem) {
819
- this.hoveredValue = newHoveredElem.getAttribute('data-value')
820
- const topPos = newHoveredElem.offsetTop
821
- this.$refs.dropdown.$el.scrollTop = topPos
822
- }
823
- }
824
- }
825
- },
826
- computed: {
827
- optionLength() {
828
- if (this.isDropdownOpen) {
829
- return this.$refs.dropdown.$el.childElementCount > 1
830
- ? this.$refs.dropdown.$el.childElementCount
831
- : this.$refs.dropdown.$el.children[0].childElementCount
832
- }
833
-
834
- return 0
835
- },
836
- isSearchBarVisible() {
837
- return (
838
- this.isSearchable &&
839
- this.optionLength >= this.minOptionLength &&
840
- this.isDropdownOpen
841
- )
842
- },
843
- computedWidth() {
844
- function removePX(item) {
845
- return Number(item.replace('px', ''))
846
- }
847
-
848
- return this.selectWidth === '100%'
849
- ? '100%'
850
- : removePX(this.selectWidth) - removePX(CARET_WIDTH) + 'px'
851
- },
852
- getOptionWidth() {
853
- if (this.optionWidth) return this.optionWidth
854
-
855
- return this.dropdownWidth
856
- },
857
- isSelectDropdownShown() {
858
- return (
859
- this.isDropdownOpen &&
860
- this.dropdownPosition.left !== null &&
861
- (!this.isSearchable || this.isSearchable)
862
- )
863
- },
864
- isMobileDevice() {
865
- const userAgent = navigator.userAgent || navigator.vendor || window.opera
866
- const touchCapable =
867
- 'ontouchstart' in window ||
868
- navigator.maxTouchPoints > 0 ||
869
- navigator.msMaxTouchPoints > 0
870
-
871
- return (
872
- /Android/i.test(userAgent) ||
873
- /iPad|iPhone|iPod/.test(userAgent) ||
874
- (/Macintosh/.test(userAgent) && touchCapable) ||
875
- /windows phone/i.test(userAgent)
876
- )
877
- }
878
- },
879
- watch: {
880
- value(val) {
881
- this.selectedValue = val
882
- },
883
- async isDropdownOpen(val) {
884
- if (val) {
885
- this.$emit('on-dropdown-open')
886
- setTimeout(() => {
887
- this.isClickOutsideActive = true
888
- }, 10)
889
- await this.$nextTick()
890
- this.handleSetDropdownOffet()
891
- } else {
892
- this.dropdownPosition.left = null
893
- setTimeout(() => {
894
- this.isClickOutsideActive = false
895
- }, 10)
896
- }
897
- if (val && this.isSearchable) {
898
- this.$nextTick(() => {
899
- if (this.$refs.searchInput && !this.isMobileDevice) {
900
- this.$refs.searchInput.$el.querySelector('input').focus()
901
- }
902
- })
903
- }
904
- }
905
- }
906
- }
907
- </script>
1
+ <template>
2
+ <Container
3
+ :selectWidth="selectWidth"
4
+ :noRelative="noRelative"
5
+ @mouseenter="mouseEnterHandler"
6
+ @mouseleave="mouseLeaveHandler"
7
+ >
8
+ <input-wrapper
9
+ :hasLabel="!!label && label.length > 0"
10
+ :alignItems="alignItems"
11
+ :noRelative="noRelative"
12
+ >
13
+ <label-wrapper
14
+ v-if="label"
15
+ :data-id="labelDataId"
16
+ :infoTextMessage="!!infoTextMessage"
17
+ >
18
+ <input-label
19
+ :fontColor="
20
+ labelFontColor || colorMode == 'dark' ? 'white' : 'eturnityGrey'
21
+ "
22
+ :fontSize="fontSize"
23
+ >{{ label }}
24
+ <optionalLabel v-if="labelOptional">
25
+ ({{ $gettext('Optional') }})</optionalLabel
26
+ >
27
+ </input-label>
28
+ <info-text
29
+ v-if="infoTextMessage || !!this.$slots.infoText"
30
+ :text="infoTextMessage"
31
+ :size="infoTextSize"
32
+ :alignArrow="infoTextAlignArrow"
33
+ :width="infoTextWidth"
34
+ :max-width="infoTextWidth"
35
+ >
36
+ <slot name="infoText" />
37
+ </info-text>
38
+ </label-wrapper>
39
+ <select-button-wrapper :disabled="disabled">
40
+ <selectButton
41
+ ref="select"
42
+ class="select-button"
43
+ @click="toggleDropdown"
44
+ :selectWidth="selectWidth"
45
+ :selectHeight="selectHeight"
46
+ :height="height"
47
+ :selectMinHeight="selectMinHeight"
48
+ :bgColor="
49
+ buttonBgColor || colorMode == 'dark' ? 'transparentBlack1' : 'white'
50
+ "
51
+ :fontColor="
52
+ buttonFontColor || colorMode == 'dark' ? 'white' : 'black'
53
+ "
54
+ :fontSize="fontSize"
55
+ :hasError="hasError"
56
+ :hasNoPadding="isSearchBarVisible || !hasSelectButtonPadding"
57
+ :disabled="disabled"
58
+ @keydown="onKeyDown"
59
+ :showBorder="showBorder"
60
+ :data-id="dataId"
61
+ :paddingLeft="paddingLeft"
62
+ :tablePaddingLeft="tablePaddingLeft"
63
+ :noRelative="noRelative"
64
+ :showDisabledBackground="showDisabledBackground"
65
+ >
66
+ <draggableInputHandle
67
+ v-if="isDraggable && !isSearchBarVisible"
68
+ :height="selectHeight"
69
+ />
70
+ <inputText
71
+ v-if="isSearchBarVisible"
72
+ ref="searchInput"
73
+ tabindex="0"
74
+ inputHeight="34px"
75
+ :noBorder="true"
76
+ :fontSize="fontSize"
77
+ backgroundColor="transparent"
78
+ :fontColor="
79
+ buttonFontColor || colorMode == 'dark' ? 'white' : 'black'
80
+ "
81
+ :value="textSearch"
82
+ :inputWidth="computedWidth"
83
+ @input-change="searchChange"
84
+ @click.stop="onInputClick"
85
+ />
86
+ <selector
87
+ v-else
88
+ :showBorder="showBorder"
89
+ :selectWidth="selectWidth"
90
+ :paddingLeft="paddingLeft"
91
+ >
92
+ <slot name="selector" :selectedValue="selectedValue"></slot>
93
+ </selector>
94
+ <Caret @click.stop="toggleCaretDropdown" class="caret_dropdown">
95
+ <icon
96
+ v-if="isDropdownOpen"
97
+ name="arrow_up"
98
+ size="12px"
99
+ :color="
100
+ caretColor || colorMode == 'dark'
101
+ ? 'white'
102
+ : 'transparentBlack1'
103
+ "
104
+ />
105
+ <icon
106
+ v-else
107
+ name="arrow_down"
108
+ size="12px"
109
+ :color="
110
+ caretColor || colorMode == 'dark'
111
+ ? 'white'
112
+ : 'transparentBlack1'
113
+ "
114
+ />
115
+ </Caret>
116
+ </selectButton>
117
+ <DropdownWrapper ref="dropdownWrapperRef" :noRelative="noRelative">
118
+ <Teleport to="#portal-target">
119
+ <selectDropdown
120
+ ref="dropdown"
121
+ v-show="isSelectDropdownShown"
122
+ :dropdownPosition="dropdownPosition"
123
+ :hoveredIndex="hoveredIndex"
124
+ :hoveredValue="hoveredValue"
125
+ :isActive="isActive"
126
+ :optionWidth="getOptionWidth"
127
+ :hoveredBgColor="colorMode == 'dark' ? 'grey6' : dropdownBgColor"
128
+ :bgColor="
129
+ dropdownBgColor || colorMode == 'dark' ? 'black' : 'white'
130
+ "
131
+ :fontColor="
132
+ dropdownFontColor || colorMode == 'dark' ? 'white' : 'black'
133
+ "
134
+ :cursor="showNoItemsInfoBox ? 'default' : 'pointer'"
135
+ :noRelative="noRelative"
136
+ :fontSize="fontSize"
137
+ :minWidth="minWidth"
138
+ :selectedValue="selectedValue"
139
+ @option-selected="optionSelected"
140
+ @option-hovered="optionHovered"
141
+ @mouseleave="optionLeave"
142
+ >
143
+ <slot name="dropdown"></slot>
144
+ <NoItemsInfoBox
145
+ v-if="showNoItemsInfoBox"
146
+ :backgroundColor="colorMode == 'dark' ? 'black' : 'white'"
147
+ :fontSize="fontSize"
148
+ >
149
+ {{ $gettext('No items found.') }}
150
+ </NoItemsInfoBox>
151
+ </selectDropdown>
152
+ </Teleport>
153
+ </DropdownWrapper>
154
+ </select-button-wrapper>
155
+ </input-wrapper>
156
+ </Container>
157
+ </template>
158
+
159
+ <script>
160
+ //How to use it
161
+ // <Select
162
+ // hoverDropdown="true"
163
+ // selectWidth="100%"
164
+ // minWidth="220px"
165
+ // optionWidth="50%"
166
+ // label="that is a label"
167
+ // alignItems="vertical"
168
+ // label-data-id="test-label-data-id"
169
+ // data-id="test-data-id"
170
+ // :hasSelectButtonPadding="false"
171
+ // >
172
+ // <template #selector="{selectedValue}">
173
+ // value selected: {{selectedValue}}
174
+ // </template>
175
+ // <template #dropdown>
176
+ // <Option value="1">value one</Option>
177
+ // <Option value="2">value two</Option>
178
+ // <Option value="3">value three</Option>
179
+ // <Option value="4">value four</Option>
180
+ // </template>
181
+ // </Select>
182
+
183
+ import { Teleport } from 'vue'
184
+ import styled from 'vue3-styled-components'
185
+ import InfoText from '../../infoText'
186
+ import icon from '../../icon'
187
+ import inputText from '../inputText'
188
+ import draggableInputHandle from '../../draggableInputHandle'
189
+
190
+ const CARET_WIDTH = '30px'
191
+ const BORDER_WIDTH = '1px'
192
+
193
+ const Caret = styled.div`
194
+ display: flex;
195
+ align-items: center;
196
+ justify-content: center;
197
+ width: ${CARET_WIDTH};
198
+ min-width: ${CARET_WIDTH};
199
+ height: 100%;
200
+ align-items: center;
201
+ cursor: pointer;
202
+ margin-left: auto;
203
+ `
204
+
205
+ const selectorProps = {
206
+ selectWidth: String,
207
+ paddingLeft: String,
208
+ showBorder: Boolean
209
+ }
210
+ const Selector = styled('div', selectorProps)`
211
+ ${(props) =>
212
+ props.selectWidth === '100%'
213
+ ? 'width: 100%;'
214
+ : `width: calc(${props.selectWidth} -
215
+ (
216
+ ${CARET_WIDTH} +
217
+ ${props.paddingLeft}
218
+ ${props.showBorder ? `+ (${BORDER_WIDTH} * 2)` : ''}
219
+ )
220
+ );
221
+ white-space: nowrap;
222
+ text-overflow: ellipsis;
223
+ overflow: hidden;`}
224
+ `
225
+
226
+ const labelAttrs = { fontSize: String, fontColor: String }
227
+ const InputLabel = styled('div', labelAttrs)`
228
+ color: ${(props) =>
229
+ props.theme.colors[props.fontColor]
230
+ ? props.theme.colors[props.fontColor]
231
+ : props.fontColor};
232
+ font-size: ${(props) => props.fontSize};
233
+ font-weight: 700;
234
+ `
235
+ const optionalLabel = styled.span`
236
+ font-weight: 300;
237
+ `
238
+ const inputProps = {
239
+ selectWidth: String,
240
+ optionWidth: String,
241
+ noRelative: Boolean
242
+ }
243
+ const Container = styled('div', inputProps)`
244
+ width: ${(props) => props.selectWidth};
245
+ position: ${(props) => (props.noRelative ? 'static' : 'relative')};
246
+ display: inline-block;
247
+ `
248
+
249
+ const LabelWrapperAttrs = { infoTextMessage: Boolean }
250
+ const LabelWrapper = styled('div', LabelWrapperAttrs)`
251
+ display: inline-grid;
252
+ grid-template-columns: ${(props) =>
253
+ props.infoTextMessage ? 'auto auto' : 'auto'};
254
+ grid-gap: 12px;
255
+ align-items: center;
256
+ justify-content: start;
257
+ `
258
+
259
+ const SelectButtonWrapperAttrs = {
260
+ disabled: Boolean
261
+ }
262
+ const SelectButtonWrapper = styled('div', SelectButtonWrapperAttrs)`
263
+ ${(props) => (props.disabled ? 'cursor: not-allowed' : 'cursor: pointer')};
264
+ `
265
+
266
+ const selectButtonAttrs = {
267
+ bgColor: String,
268
+ fontColor: String,
269
+ hasError: Boolean,
270
+ disabled: Boolean,
271
+ selectHeight: String,
272
+ selectWidth: String,
273
+ height: String,
274
+ selectMinHeight: String,
275
+ hasNoPadding: Boolean,
276
+ showBorder: Boolean,
277
+ paddingLeft: String,
278
+ noRelative: Boolean,
279
+ tablePaddingLeft: String,
280
+ showDisabledBackground: Boolean
281
+ }
282
+ const selectButton = styled('div', selectButtonAttrs)`
283
+ position: ${(props) => (props.noRelative ? 'static' : 'relative')};
284
+ box-sizing: border-box;
285
+ border-radius: 4px;
286
+ max-width: ${(props) => (props.selectWidth ? props.selectWidth : '100%')};
287
+ ${(props) =>
288
+ props.isSearchBarVisible
289
+ ? ''
290
+ : `padding-left: ${
291
+ props.hasNoPadding
292
+ ? '0'
293
+ : props.tablePaddingLeft
294
+ ? props.tablePaddingLeft
295
+ : props.paddingLeft
296
+ }`};
297
+ text-align: left;
298
+ min-height: ${(props) =>
299
+ props.selectHeight
300
+ ? props.selectHeight
301
+ : props.selectMinHeight
302
+ ? props.selectMinHeight
303
+ : props.height
304
+ ? props.height
305
+ : '36px'};
306
+ display: flex;
307
+ align-items: center;
308
+ height: ${(props) => props.selectHeight};
309
+ ${({ showBorder, theme, hasError }) =>
310
+ showBorder &&
311
+ `
312
+ border: ${BORDER_WIDTH} solid ${
313
+ hasError ? theme.colors.red : theme.colors.grey4
314
+ }
315
+ `}
316
+ background-color:${(props) =>
317
+ props.disabled && props.showDisabledBackground
318
+ ? props.theme.colors.grey5
319
+ : props.theme.colors[props.bgColor]
320
+ ? props.theme.colors[props.bgColor]
321
+ : props.bgColor};
322
+ color: ${(props) =>
323
+ props.theme.colors[props.fontColor]
324
+ ? props.theme.colors[props.fontColor]
325
+ : props.fontColor};
326
+ ${(props) => (props.disabled ? 'pointer-events: none' : '')};
327
+ overflow: hidden;
328
+ & > .handle {
329
+ border-right: ${(props) =>
330
+ props.hasError ? props.theme.colors.red : props.theme.colors.grey4}
331
+ 1px solid;
332
+ }
333
+ `
334
+ const selectDropdownAttrs = {
335
+ hoveredBgColor: String,
336
+ bgColor: String,
337
+ fontColor: String,
338
+ optionWidth: String,
339
+ hoveredIndex: Number,
340
+ fontSize: String,
341
+ dropdownPosition: Object,
342
+ hoveredValue: Number | String,
343
+ selectedValue: Number | String,
344
+ noRelative: Boolean,
345
+ minWidth: String,
346
+ cursor: String
347
+ }
348
+ const selectDropdown = styled('div', selectDropdownAttrs)`
349
+ box-sizing: border-box;
350
+ z-index: ${(props) => (props.isActive ? '2' : '99999')};
351
+ position: absolute;
352
+ top: ${(props) =>
353
+ props.noRelative ? 'auto' : props.dropdownPosition?.top + 'px'};
354
+ left: ${(props) => props.dropdownPosition?.left}px;
355
+ border: ${BORDER_WIDTH} solid ${(props) => props.theme.colors.grey4};
356
+ border-radius: 4px;
357
+ display: flex;
358
+ flex-direction: column;
359
+ align-items: flex-start;
360
+ padding: 0px;
361
+ box-shadow: 0px 8px 16px 0px rgba(0, 0, 0, 0.2);
362
+ width: ${(props) => (props.optionWidth ? props.optionWidth : '100%')};
363
+ min-width: ${(props) =>
364
+ props.minWidth
365
+ ? props.minWidth
366
+ : props.optionWidth
367
+ ? props.optionWidth
368
+ : '100%'};
369
+ background-color: ${(props) =>
370
+ props.theme.colors[props.bgColor]
371
+ ? props.theme.colors[props.bgColor]
372
+ : props.bgColor};
373
+ color: ${(props) =>
374
+ props.theme.colors[props.fontColor]
375
+ ? props.theme.colors[props.fontColor]
376
+ : props.fontColor};
377
+ max-height: 300px;
378
+ overflow-y: auto;
379
+ cursor: ${(props) => props.cursor};
380
+
381
+ & > div[data-value='${(props) => props.hoveredValue}'] {
382
+ background-color: ${(props) =>
383
+ props.theme.colors[props.hoveredBgColor]
384
+ ? props.theme.colors[props.hoveredBgColor]
385
+ : props.hoveredBgColor};
386
+ }
387
+ font-size: ${(props) => props.fontSize};
388
+ `
389
+ selectDropdown.emits = ['option-hovered', 'option-selected']
390
+ const DropdownAttrs = { noRelative: Boolean }
391
+ const DropdownWrapper = styled('div', DropdownAttrs)`
392
+ position: ${(props) => (props.noRelative ? 'static' : 'relative')};
393
+ `
394
+ const inputAttrs = {
395
+ alignItems: String,
396
+ hasLabel: Boolean,
397
+ noRelative: Boolean
398
+ }
399
+ const InputWrapper = styled('div', inputAttrs)`
400
+ position: ${(props) => (props.noRelative ? 'static' : 'relative')};
401
+ display: grid;
402
+ width: 100%;
403
+ min-width: ${(props) => (props.minWidth ? props.minWidth : '150px')};
404
+ align-items: center;
405
+ gap: 8px;
406
+ grid-template-columns: ${(props) =>
407
+ props.alignItems === 'vertical' || !props.hasLabel ? '1fr' : 'auto 1fr'};
408
+ `
409
+
410
+ const noItemsInfoBoxAttrs = {
411
+ backgroundColor: String,
412
+ fontSize: String
413
+ }
414
+ const NoItemsInfoBox = styled('div', noItemsInfoBoxAttrs)`
415
+ width: 100%;
416
+ padding: 12px 10px;
417
+ background-color: ${(props) =>
418
+ props.theme.colors[props.backgroundColor]
419
+ ? props.theme.colors[props.backgroundColor]
420
+ : props.backgroundColor};
421
+ font-size: ${(props) => props.fontSize};
422
+ color: inherit;
423
+ `
424
+
425
+ const DROPDOWN_HEIGHT_OFFSET = 4
426
+ const DROPDOWN_TOP_OFFSET = 21
427
+ const MIN_OPTION_LENGTH = 5
428
+
429
+ const DROPDOWN_MENU_POSITIONS = {
430
+ Automatic: 'automatic',
431
+ Bottom: 'bottom'
432
+ }
433
+
434
+ export default {
435
+ name: 'RCselect',
436
+
437
+ props: {
438
+ value: {
439
+ required: false,
440
+ default: null
441
+ },
442
+ fontSize: {
443
+ required: false,
444
+ default: '13px'
445
+ },
446
+ noRelative: {
447
+ required: false,
448
+ default: false
449
+ },
450
+ label: {
451
+ required: false
452
+ },
453
+ labelOptional: {
454
+ required: false,
455
+ default: false
456
+ },
457
+ labelDataId: {
458
+ required: false,
459
+ default: ''
460
+ },
461
+ infoTextMessage: {
462
+ required: false
463
+ },
464
+ selectWidth: {
465
+ type: String,
466
+ required: false,
467
+ default: '100%'
468
+ },
469
+ minWidth: {
470
+ required: false
471
+ },
472
+ maxWidth: {
473
+ required: false
474
+ },
475
+ selectHeight: {
476
+ type: String,
477
+ required: false,
478
+ default: null
479
+ },
480
+ height: {
481
+ required: false,
482
+ default: null
483
+ },
484
+ selectMinHeight: {
485
+ required: false,
486
+ default: '36px'
487
+ },
488
+ optionWidth: {
489
+ required: false,
490
+ default: null
491
+ },
492
+ hoverDropdown: {
493
+ required: false,
494
+ default: false
495
+ },
496
+ dropdownAutoClose: {
497
+ required: false,
498
+ default: false
499
+ },
500
+ alignItems: {
501
+ required: false,
502
+ default: 'horizontal'
503
+ },
504
+ buttonBgColor: {
505
+ required: false
506
+ },
507
+ buttonFontColor: {
508
+ required: false
509
+ },
510
+ dropdownBgColor: {
511
+ required: false,
512
+ default: 'grey5'
513
+ },
514
+ dropdownFontColor: {
515
+ required: false
516
+ },
517
+ dropDownArrowVisible: {
518
+ required: false,
519
+ default: true
520
+ },
521
+ caretColor: {
522
+ required: false
523
+ },
524
+ labelFontColor: {
525
+ required: false
526
+ },
527
+ colorMode: {
528
+ required: false,
529
+ default: 'light'
530
+ },
531
+ isSearchable: {
532
+ required: false,
533
+ default: true
534
+ },
535
+ hasError: {
536
+ required: false,
537
+ default: false
538
+ },
539
+ disabled: {
540
+ required: false,
541
+ default: false
542
+ },
543
+ showBorder: {
544
+ required: false,
545
+ default: true
546
+ },
547
+ infoTextSize: {
548
+ required: false,
549
+ default: '14px'
550
+ },
551
+ dataId: {
552
+ type: String,
553
+ default: ''
554
+ },
555
+ hasSelectButtonPadding: {
556
+ required: false,
557
+ type: Boolean,
558
+ default: true
559
+ },
560
+ isDraggable: {
561
+ type: Boolean,
562
+ default: false
563
+ },
564
+ leftPadding: {
565
+ type: String,
566
+ default: '15px'
567
+ },
568
+ tablePaddingLeft: {
569
+ required: false
570
+ },
571
+ showDisabledBackground: {
572
+ required: false,
573
+ default: true
574
+ },
575
+ minOptionLength: {
576
+ type: Number,
577
+ default: MIN_OPTION_LENGTH
578
+ },
579
+ dropdownMenuPosition: {
580
+ type: String,
581
+ default: DROPDOWN_MENU_POSITIONS.Automatic // options: ['automatic', bottom]
582
+ },
583
+ infoTextWidth: {
584
+ type: String,
585
+ required: false
586
+ },
587
+ infoTextAlignArrow: {
588
+ type: String,
589
+ required: false
590
+ },
591
+ // used to trigger the options retrieving. useful for cases when options can change
592
+ haveOptionsUpdated: {
593
+ required: false,
594
+ default: 0
595
+ }
596
+ },
597
+
598
+ components: {
599
+ selectButton,
600
+ SelectButtonWrapper,
601
+ selectDropdown,
602
+ Container,
603
+ InputLabel,
604
+ LabelWrapper,
605
+ optionalLabel,
606
+ InfoText,
607
+ InputWrapper,
608
+ NoItemsInfoBox,
609
+ DropdownWrapper,
610
+ icon,
611
+ Caret,
612
+ Selector,
613
+ inputText,
614
+ Teleport,
615
+ draggableInputHandle
616
+ },
617
+
618
+ data() {
619
+ return {
620
+ selectedValue: null,
621
+ paddingLeft: this.isDraggable ? '30px' : this.leftPadding,
622
+ isDropdownOpen: false,
623
+ isActive: false,
624
+ textSearch: '',
625
+ hoveredIndex: 0,
626
+ hoveredValue: null,
627
+ isClickOutsideActive: false,
628
+ dropdownPosition: {
629
+ left: null,
630
+ top: null
631
+ },
632
+ dropdownWidth: null,
633
+ dropdownChildrenElements: [],
634
+ filteredDropdownChildrenElements: []
635
+ }
636
+ },
637
+ mounted() {
638
+ this.getDropdownChildrenElements()
639
+ this.filteredDropdownChildrenElements = this.dropdownChildrenElements
640
+ this.resetHoveredOption()
641
+ this.observeDropdownHeight()
642
+ this.observeSelectWidth()
643
+ window.addEventListener('resize', this.handleSetDropdownOffet)
644
+ window.addEventListener('keydown', this.onKeyDown)
645
+ },
646
+ beforeMount() {
647
+ this.selectedValue = this.value
648
+ document.addEventListener('click', this.clickOutside)
649
+ this.getDropdownPosition()
650
+ window.removeEventListener('resize', this.handleSetDropdownOffet)
651
+ if (this.dropdownResizeObserver) this.dropdownResizeObserver.disconnect()
652
+ if (this.selectResizeObserver) this.selectResizeObserver.disconnect()
653
+ },
654
+ unmounted() {
655
+ document.removeEventListener('click', this.clickOutside)
656
+ window.removeEventListener('keydown', this.onKeyDown)
657
+ },
658
+ methods: {
659
+ focus() {
660
+ this.isActive = true
661
+ },
662
+ blur(e) {
663
+ this.isActive = false
664
+ this.$emit('blur', e)
665
+ },
666
+ toggleDropdown() {
667
+ this.isDropdownOpen = !this.isDropdownOpen
668
+ },
669
+ toggleCaretDropdown() {
670
+ this.isDropdownOpen = !this.isDropdownOpen
671
+ },
672
+ resetHoveredOption() {
673
+ this.value !== null
674
+ ? this.optionHovered(this.selectedValue)
675
+ : this.hoverFirstFilteredOption()
676
+ },
677
+ closeDropdown() {
678
+ this.blur()
679
+ this.isDropdownOpen = false
680
+ },
681
+ resetInteractionResults() {
682
+ if (this.isSearchable) {
683
+ this.searchChange('')
684
+ }
685
+ this.resetHoveredOption()
686
+ },
687
+ optionSelected(e) {
688
+ this.selectedValue = e
689
+ this.closeDropdown()
690
+ this.blur()
691
+ this.$emit('input-change', e)
692
+ },
693
+ optionHovered(e) {
694
+ this.hoveredValue = e
695
+ },
696
+ mouseEnterHandler() {
697
+ if (this.hoverDropdown) {
698
+ this.focus()
699
+ this.isDropdownOpen = true
700
+ }
701
+ },
702
+ mouseLeaveHandler() {
703
+ if (this.hoverDropdown) {
704
+ this.blur()
705
+ }
706
+ },
707
+ optionLeave() {
708
+ if (this.dropdownAutoClose) {
709
+ this.isDropdownOpen = false
710
+ }
711
+ },
712
+ getDropdownChildrenElements() {
713
+ this.dropdownChildrenElements = [
714
+ ...this.$refs.dropdown.$el.children
715
+ ].filter((el) => el.dataset.value)
716
+ },
717
+ searchChange(value) {
718
+ this.filteredDropdownChildrenElements = []
719
+
720
+ if (this.textSearch !== value) {
721
+ this.$emit('search-change', value)
722
+ }
723
+ this.textSearch = value
724
+
725
+ this.dropdownChildrenElements.forEach((el) => {
726
+ if (!el.textContent.toLowerCase().includes(value.toLowerCase())) {
727
+ el.style.display = 'none'
728
+ return
729
+ }
730
+
731
+ this.filteredDropdownChildrenElements.push(el)
732
+ el.style.display = 'inherit'
733
+ })
734
+
735
+ if (
736
+ value &&
737
+ !this.hoveredOptionText.toLowerCase().includes(value.toLowerCase())
738
+ ) {
739
+ this.hoverFirstFilteredOption()
740
+ }
741
+
742
+ this.scrollDropdownToHoveredOption()
743
+ },
744
+ hoverFirstFilteredOption() {
745
+ if (this.filteredDropdownChildrenElements.length) {
746
+ this.optionHovered(
747
+ this.filteredDropdownChildrenElements[0].dataset.value
748
+ )
749
+ }
750
+ },
751
+ clickOutside(event) {
752
+ const dropdownRef = this.$refs.dropdown
753
+ // we need to prevent closing on selecting an option, because in the case of
754
+ // a disabled option, we don't want to close the dropdown
755
+ if (!this.isClickOutsideActive) return
756
+ if (
757
+ this.$refs.select.$el == event.target ||
758
+ this.$refs.select.$el.contains(event.target) ||
759
+ event.target.id === 'more-button' ||
760
+ event.target.parentNode === dropdownRef.$el
761
+ ) {
762
+ return
763
+ } else {
764
+ this.closeDropdown()
765
+ }
766
+ },
767
+ onKeyDown(e) {
768
+ if (this.isDropdownOpen) {
769
+ if (e.key == 'ArrowDown') {
770
+ this.onArrowPress(1)
771
+ } else if (e.key == 'ArrowUp') {
772
+ this.onArrowPress(-1)
773
+ } else if (e.key == 'Enter') {
774
+ this.optionSelected(this.hoveredValue)
775
+ } else if (e.key == 'Escape') {
776
+ this.closeDropdown()
777
+ }
778
+ e.stopPropagation()
779
+ e.preventDefault()
780
+ }
781
+ },
782
+ // If some part of the dropdown menu is outside viewport of the bottom of the screen,
783
+ // we need to offset it and display it at the top of the select dropdown instead
784
+ async getDropdownPosition() {
785
+ if (
786
+ !this.$refs.dropdownWrapperRef ||
787
+ !this.$refs.select ||
788
+ !this.$refs.dropdown
789
+ ) {
790
+ return
791
+ }
792
+ await this.$nextTick()
793
+ const isDisplayedAtBottom = await this.generateDropdownPosition()
794
+ // If the dropdown menu is going to be displayed at the bottom,
795
+ // we need reverify its position after a dom update (nextTick)
796
+ await this.$nextTick()
797
+ if (isDisplayedAtBottom) this.generateDropdownPosition()
798
+ },
799
+ async generateDropdownPosition() {
800
+ const isDropdownNotCompletelyVisible =
801
+ await this.isBottomOfDropdownOutOfViewport()
802
+ const dropdownWrapperEl = this.$refs.dropdownWrapperRef.$el
803
+ const selectButtonHeight = this.$refs.select.$el.clientHeight
804
+ const dropdownHeight = this.$refs.dropdown.$el.clientHeight
805
+ const dropdownWrapperRelativeHeight =
806
+ dropdownWrapperEl.getBoundingClientRect().top +
807
+ window.scrollY +
808
+ DROPDOWN_HEIGHT_OFFSET
809
+
810
+ const top =
811
+ isDropdownNotCompletelyVisible ||
812
+ (!isDropdownNotCompletelyVisible &&
813
+ this.dropdownMenuPosition === DROPDOWN_MENU_POSITIONS.Bottom)
814
+ ? dropdownWrapperRelativeHeight
815
+ : dropdownWrapperRelativeHeight -
816
+ dropdownHeight -
817
+ selectButtonHeight -
818
+ DROPDOWN_TOP_OFFSET
819
+ const left = this.dropdownPosition.left
820
+ ? this.dropdownPosition.left
821
+ : dropdownWrapperEl.getBoundingClientRect().left + window.scrollX
822
+
823
+ this.dropdownPosition = { left: Math.floor(left), top: Math.floor(top) }
824
+
825
+ return isDropdownNotCompletelyVisible
826
+ },
827
+ async isBottomOfDropdownOutOfViewport() {
828
+ if (
829
+ !this.$refs.dropdown ||
830
+ this.dropdownMenuPosition === DROPDOWN_MENU_POSITIONS.Bottom
831
+ ) {
832
+ return false
833
+ }
834
+
835
+ await this.$nextTick()
836
+ const rect = this.$refs.dropdown.$el.getBoundingClientRect()
837
+ const windowHeight =
838
+ window.innerHeight || document.documentElement.clientHeight
839
+
840
+ if (windowHeight <= 650) return true
841
+
842
+ // using Math.floor because the offsets may contain decimals we are not going to consider here
843
+ return Math.floor(rect.top) + Math.floor(rect.height) <= windowHeight
844
+ },
845
+ observeDropdownHeight() {
846
+ if (!this.$refs.dropdown) return
847
+ this.dropdownResizeObserver = new ResizeObserver(() => {
848
+ this.$nextTick(() => this.getDropdownPosition())
849
+ })
850
+ this.dropdownResizeObserver.observe(this.$refs.dropdown.$el)
851
+ },
852
+ handleSetDropdownOffet() {
853
+ if (!this.$refs.select) return
854
+ this.dropdownPosition.left = Math.floor(
855
+ this.$refs.select.$el.getBoundingClientRect().left
856
+ )
857
+ this.getDropdownWidth()
858
+ },
859
+ observeSelectWidth() {
860
+ if (!this.$refs.select) return
861
+ this.selectResizeObserver = new ResizeObserver(() =>
862
+ // eslint-disable-next-line vue/valid-next-tick
863
+ this.$nextTick(() => this.getDropdownWidth())
864
+ )
865
+ this.selectResizeObserver.observe(this.$refs.dropdown.$el)
866
+ },
867
+ async getDropdownWidth() {
868
+ if (!this.$refs.select) return
869
+ await this.$nextTick()
870
+ this.dropdownWidth = `${this.$refs.select.$el.clientWidth}px`
871
+ },
872
+ onArrowPress(dir) {
873
+ if (this.filteredDropdownChildrenElements.length) {
874
+ const currentHoveredElem = this.$refs.dropdown.$el.querySelector(
875
+ `[data-value="${this.hoveredValue}"]`
876
+ )
877
+
878
+ if (currentHoveredElem) {
879
+ let newHoveredElem = this.nextHighlightedOption(
880
+ currentHoveredElem,
881
+ dir
882
+ )
883
+
884
+ if (newHoveredElem) {
885
+ this.optionHovered(newHoveredElem.getAttribute('data-value'))
886
+ this.scrollDropdownToHoveredOption()
887
+ }
888
+ }
889
+ }
890
+ },
891
+ nextHighlightedOption(currentFocusedElem, dir) {
892
+ let nextFocusedElem =
893
+ dir > 0
894
+ ? currentFocusedElem.nextElementSibling
895
+ : currentFocusedElem.previousElementSibling
896
+
897
+ if (nextFocusedElem?.style.display === 'none') {
898
+ nextFocusedElem = this.nextHighlightedOption(nextFocusedElem, dir)
899
+ }
900
+
901
+ return nextFocusedElem
902
+ },
903
+ scrollDropdownToHoveredOption() {
904
+ if (!this.hoveredValue) {
905
+ return
906
+ }
907
+
908
+ const optionTopPos = this.filteredDropdownChildrenElements.find((el) => {
909
+ const optionValueTransformed = el.dataset.value.toString().toLowerCase()
910
+ const hoveredValueTransformed = this.hoveredValue
911
+ .toString()
912
+ .toLowerCase()
913
+
914
+ return optionValueTransformed === hoveredValueTransformed
915
+ })?.offsetTop
916
+
917
+ if (typeof optionTopPos === 'number') {
918
+ this.$refs.dropdown.$el.scrollTop = optionTopPos
919
+ }
920
+ }
921
+ },
922
+ computed: {
923
+ optionLength() {
924
+ if (this.isDropdownOpen) {
925
+ return this.$refs.dropdown.$el.childElementCount > 1
926
+ ? this.$refs.dropdown.$el.childElementCount
927
+ : this.$refs.dropdown.$el.children[0].childElementCount
928
+ }
929
+
930
+ return 0
931
+ },
932
+ isSearchBarVisible() {
933
+ return (
934
+ this.isSearchable &&
935
+ this.optionLength >= this.minOptionLength &&
936
+ this.isDropdownOpen
937
+ )
938
+ },
939
+ computedWidth() {
940
+ function removePX(item) {
941
+ return Number(item.replace('px', ''))
942
+ }
943
+
944
+ return this.selectWidth === '100%'
945
+ ? '100%'
946
+ : removePX(this.selectWidth) - removePX(CARET_WIDTH) + 'px'
947
+ },
948
+ getOptionWidth() {
949
+ if (this.optionWidth) return this.optionWidth
950
+
951
+ return this.dropdownWidth
952
+ },
953
+ isSelectDropdownShown() {
954
+ return this.isDropdownOpen && this.dropdownPosition.left !== null
955
+ },
956
+ isMobileDevice() {
957
+ const userAgent = navigator.userAgent || navigator.vendor || window.opera
958
+ const touchCapable =
959
+ 'ontouchstart' in window ||
960
+ navigator.maxTouchPoints > 0 ||
961
+ navigator.msMaxTouchPoints > 0
962
+
963
+ return (
964
+ /Android/i.test(userAgent) ||
965
+ /iPad|iPhone|iPod/.test(userAgent) ||
966
+ (/Macintosh/.test(userAgent) && touchCapable) ||
967
+ /windows phone/i.test(userAgent)
968
+ )
969
+ },
970
+ selectedOptionText() {
971
+ return this.selectedValue
972
+ ? this.$refs.dropdown.$el
973
+ .querySelector(`[data-value="${this.selectedValue}"]`)
974
+ .textContent.trim()
975
+ : '-'
976
+ },
977
+ hoveredOptionText() {
978
+ return this.$refs.dropdown.$el
979
+ .querySelector(`[data-value="${this.hoveredValue}"]`)
980
+ .textContent.trim()
981
+ },
982
+ showNoItemsInfoBox() {
983
+ return (
984
+ this.dropdownChildrenElements.length &&
985
+ !this.filteredDropdownChildrenElements.length
986
+ )
987
+ }
988
+ },
989
+ watch: {
990
+ value(val) {
991
+ this.selectedValue = val
992
+ this.resetHoveredOption()
993
+ },
994
+ async isDropdownOpen(val) {
995
+ if (val) {
996
+ this.$emit('on-dropdown-open')
997
+ this.$nextTick(() => {
998
+ if (this.$refs.searchInput && !this.isMobileDevice) {
999
+ this.$refs.searchInput.$el.querySelector('input').focus()
1000
+ }
1001
+ })
1002
+ setTimeout(() => {
1003
+ this.isClickOutsideActive = true
1004
+ }, 10)
1005
+ await this.$nextTick()
1006
+ this.handleSetDropdownOffet()
1007
+ } else {
1008
+ this.dropdownPosition.left = null
1009
+
1010
+ this.resetInteractionResults()
1011
+
1012
+ setTimeout(() => {
1013
+ this.isClickOutsideActive = false
1014
+ }, 10)
1015
+ }
1016
+ this.$nextTick(() => {
1017
+ this.scrollDropdownToHoveredOption()
1018
+ })
1019
+ },
1020
+ haveOptionsUpdated() {
1021
+ this.$nextTick(() => {
1022
+ this.getDropdownChildrenElements()
1023
+ this.resetInteractionResults()
1024
+ })
1025
+ }
1026
+ }
1027
+ }
1028
+ </script>