@eturnity/eturnity_reusable_components 7.39.4-EPDM-11313.0 → 7.39.4-EPDM-12383.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 (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>