@eturnity/eturnity_reusable_components 7.39.4-EPDM-11313.0 → 7.39.4-EPDM-12383.0

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