@eturnity/eturnity_reusable_components 7.45.5-EPDM-11313-EPDM-11314.1 → 7.45.5-qa-16-11.13

Sign up to get free protection for your applications and to get access to all the features.
Files changed (363) 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 +44 -44
  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/InfoBanner.spec.js +70 -70
  259. package/src/components/banner/infoBanner/InfoBanner.stories.js +42 -42
  260. package/src/components/banner/infoBanner/index.vue +97 -97
  261. package/src/components/buttons/buttonIcon/index.vue +145 -145
  262. package/src/components/buttons/closeButton/CloseButton.stories.js +25 -25
  263. package/src/components/buttons/closeButton/index.vue +62 -62
  264. package/src/components/buttons/mainButton/MainButton.stories.js +51 -51
  265. package/src/components/buttons/mainButton/index.vue +150 -155
  266. package/src/components/buttons/mainButton/mainButton.spec.js +35 -35
  267. package/src/components/card/Card.stories.js +79 -79
  268. package/src/components/card/card.spec.js +135 -135
  269. package/src/components/card/index.vue +116 -116
  270. package/src/components/collapsableInfoText/index.vue +127 -127
  271. package/src/components/deleteIcon/DeleteIcon.stories.js +29 -29
  272. package/src/components/deleteIcon/index.vue +78 -78
  273. package/src/components/draggableInputHandle/index.vue +46 -46
  274. package/src/components/dropdown/Dropdown.stories.js +53 -53
  275. package/src/components/dropdown/index.vue +138 -138
  276. package/src/components/errorMessage/index.vue +64 -64
  277. package/src/components/filter/filterSettings.vue +669 -669
  278. package/src/components/filter/index.vue +154 -154
  279. package/src/components/filter/parentDropdown.vue +91 -91
  280. package/src/components/icon/Icons.stories.js +41 -41
  281. package/src/components/icon/iconCache.mjs +23 -23
  282. package/src/components/icon/iconCollection.vue +82 -82
  283. package/src/components/icon/index.vue +140 -140
  284. package/src/components/iconWrapper/index.vue +179 -179
  285. package/src/components/infoCard/index.vue +67 -130
  286. package/src/components/infoText/index.vue +190 -190
  287. package/src/components/inputs/checkbox/Checkbox.stories.js +63 -63
  288. package/src/components/inputs/checkbox/checkbox.spec.js +109 -109
  289. package/src/components/inputs/checkbox/index.vue +225 -225
  290. package/src/components/inputs/inputNumber/InputNumber.stories.js +150 -150
  291. package/src/components/inputs/inputNumber/index.vue +771 -789
  292. package/src/components/inputs/inputNumberQuestion/index.vue +218 -218
  293. package/src/components/inputs/inputText/InputText.stories.js +75 -75
  294. package/src/components/inputs/inputText/index.vue +380 -380
  295. package/src/components/inputs/radioButton/RadioButton.stories.js +77 -77
  296. package/src/components/inputs/radioButton/defaultProps.js +33 -33
  297. package/src/components/inputs/radioButton/index.vue +309 -309
  298. package/src/components/inputs/radioButton/radioButton.spec.js +269 -269
  299. package/src/components/inputs/searchInput/SearchInput.stories.js +40 -40
  300. package/src/components/inputs/searchInput/index.vue +151 -151
  301. package/src/components/inputs/select/index.vue +962 -917
  302. package/src/components/inputs/select/option/index.vue +156 -156
  303. package/src/components/inputs/select/select.stories.js +58 -58
  304. package/src/components/inputs/slider/index.vue +126 -126
  305. package/src/components/inputs/switchField/index.vue +254 -254
  306. package/src/components/inputs/textAreaInput/TextAreaInput.stories.js +127 -127
  307. package/src/components/inputs/textAreaInput/index.vue +198 -198
  308. package/src/components/inputs/toggle/Toggle.stories.js +77 -77
  309. package/src/components/inputs/toggle/index.vue +317 -317
  310. package/src/components/inputs/toggle/toggle.spec.js +102 -102
  311. package/src/components/label/index.vue +99 -99
  312. package/src/components/markerItem/index.vue +88 -88
  313. package/src/components/modals/actionModal/index.vue +64 -64
  314. package/src/components/modals/infoModal/index.vue +52 -52
  315. package/src/components/modals/modal/index.vue +190 -196
  316. package/src/components/modals/modal/modal.stories.js +31 -31
  317. package/src/components/navigationTabs/index.vue +114 -114
  318. package/src/components/pageSubtitle/PageSubtitle.stories.js +59 -59
  319. package/src/components/pageSubtitle/index.vue +78 -78
  320. package/src/components/pageSubtitle/pageSubtitle.spec.js +46 -46
  321. package/src/components/pageTitle/PageTitle.stories.js +95 -95
  322. package/src/components/pageTitle/index.vue +91 -91
  323. package/src/components/pageTitle/pageTitle.spec.js +46 -46
  324. package/src/components/pagination/index.vue +148 -148
  325. package/src/components/progressBar/index.vue +125 -125
  326. package/src/components/projectMarker/index.vue +300 -300
  327. package/src/components/rangeSlider/Slider.vue +573 -573
  328. package/src/components/rangeSlider/index.vue +517 -517
  329. package/src/components/rangeSlider/utils/dom.js +49 -49
  330. package/src/components/rangeSlider/utils/fns.js +26 -26
  331. package/src/components/roundTabs/index.vue +107 -107
  332. package/src/components/selectedOptions/index.vue +471 -471
  333. package/src/components/selectedOptions/selectedOptions.spec.js +176 -176
  334. package/src/components/selectedOptions/selectedOptions.stories.js +155 -155
  335. package/src/components/sideMenu/index.vue +270 -270
  336. package/src/components/spinner/Spinner.stories.js +34 -34
  337. package/src/components/spinner/index.vue +85 -85
  338. package/src/components/spinner/spinner.spec.js +69 -69
  339. package/src/components/tableDropdown/index.vue +638 -638
  340. package/src/components/tables/mainTable/exampleNested.vue +328 -328
  341. package/src/components/tables/mainTable/index.vue +510 -510
  342. package/src/components/tables/viewTable/index.vue +195 -195
  343. package/src/components/tabsHeader/index.vue +83 -83
  344. package/src/components/threeDots/index.vue +413 -413
  345. package/src/components/videoThumbnail/index.vue +103 -103
  346. package/src/components/videoThumbnail/videoThumbnail.stories.js +33 -33
  347. package/src/helpers/currencyMapping.js +28 -28
  348. package/src/helpers/numberConverter.js +103 -103
  349. package/src/helpers/translateLang.js +128 -128
  350. package/src/main.js +7 -7
  351. package/src/mixins/inputValidations.js +97 -97
  352. package/src/router/dynamicRoutes.js +23 -23
  353. package/src/stories/Button.stories.js +48 -48
  354. package/src/stories/Button.vue +52 -52
  355. package/src/stories/Configure.mdx +364 -364
  356. package/src/stories/Header.stories.js +41 -41
  357. package/src/stories/Header.vue +59 -59
  358. package/src/stories/Page.stories.js +30 -30
  359. package/src/stories/Page.vue +83 -83
  360. package/src/stories/button.css +30 -30
  361. package/src/stories/header.css +32 -32
  362. package/src/stories/page.css +69 -69
  363. package/src/utils/index.js +12 -12
@@ -1,917 +1,962 @@
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
- :disabled="disabled"
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 || disabled
101
- ? 'grey2'
102
- : colorMode == 'dark'
103
- ? 'white'
104
- : 'transparentBlack1'
105
- "
106
- name="arrow_up"
107
- size="12px"
108
- />
109
- <Icon
110
- v-else
111
- :color="
112
- caretColor || disabled
113
- ? 'grey2'
114
- : colorMode == 'dark'
115
- ? 'white'
116
- : 'transparentBlack1'
117
- "
118
- name="arrow_down"
119
- size="12px"
120
- />
121
- </Caret>
122
- </SelectButton>
123
- <DropdownWrapper ref="dropdownWrapperRef" :no-relative="noRelative">
124
- <Teleport to="#portal-target">
125
- <SelectDropdown
126
- v-show="isSelectDropdownShown"
127
- ref="dropdown"
128
- :bg-color="
129
- dropdownBgColor || colorMode == 'dark' ? 'black' : 'white'
130
- "
131
- :dropdown-position="dropdownPosition"
132
- :font-color="
133
- dropdownFontColor || colorMode == 'dark' ? 'white' : 'black'
134
- "
135
- :font-size="fontSize"
136
- :hovered-bg-color="
137
- colorMode == 'dark' ? '#000000' : dropdownBgColor
138
- "
139
- :hovered-index="hoveredIndex"
140
- :hovered-value="hoveredValue"
141
- :is-active="isActive"
142
- :min-width="minWidth"
143
- :no-relative="noRelative"
144
- :option-width="getOptionWidth"
145
- :selected-value="selectedValue"
146
- @mouseleave="optionLeave"
147
- @option-hovered="optionHovered"
148
- @option-selected="optionSelected"
149
- >
150
- <slot name="dropdown"></slot>
151
- </SelectDropdown>
152
- </Teleport>
153
- </DropdownWrapper>
154
- </SelectButtonWrapper>
155
- </InputWrapper>
156
- </Container>
157
- </template>
158
-
159
- <script>
160
- //How to use it
161
- // <Select
162
- // hoverDropdown="true"
163
- // selectWidth="100%"
164
- // minWidth="220px"
165
- // optionWidth="50%"
166
- // label="that is a label"
167
- // alignItems="vertical"
168
- // label-data-id="test-label-data-id"
169
- // data-id="test-data-id"
170
- // :hasSelectButtonPadding="false"
171
- // >
172
- // <template #selector="{selectedValue}">
173
- // value selected: {{selectedValue}}
174
- // </template>
175
- // <template #dropdown>
176
- // <Option value="1">value one</Option>
177
- // <Option value="2">value two</Option>
178
- // <Option value="3">value three</Option>
179
- // <Option value="4">value four</Option>
180
- // </template>
181
- // </Select>
182
-
183
- import { Teleport } from 'vue'
184
- import styled from 'vue3-styled-components'
185
- import InfoText from '../../infoText'
186
- import Icon from '../../icon'
187
- import InputText from '../inputText'
188
- import DraggableInputHandle from '../../draggableInputHandle'
189
- import { debounce } from '../../../utils'
190
-
191
- const CARET_WIDTH = '30px'
192
- const BORDER_WIDTH = '1px'
193
-
194
- const Caret = styled.div`
195
- display: flex;
196
- align-items: center;
197
- justify-content: center;
198
- width: ${CARET_WIDTH};
199
- min-width: ${CARET_WIDTH};
200
- height: 100%;
201
- align-items: center;
202
- cursor: pointer;
203
- margin-left: auto;
204
- `
205
-
206
- const selectorProps = {
207
- disabled: String,
208
- selectWidth: String,
209
- paddingLeft: String,
210
- showBorder: Boolean,
211
- }
212
- const Selector = styled('div', selectorProps)`
213
- color: ${(props) => (props.disabled ? props.theme.colors.grey2 : '')};
214
- ${(props) =>
215
- props.selectWidth === '100%'
216
- ? 'width: 100%;'
217
- : `width: calc(${props.selectWidth} -
218
- (
219
- ${CARET_WIDTH} +
220
- ${props.paddingLeft}
221
- ${props.showBorder ? `+ (${BORDER_WIDTH} * 2)` : ''}
222
- )
223
- );
224
- white-space: nowrap;
225
- text-overflow: ellipsis;
226
- overflow: hidden;`}
227
- `
228
-
229
- const labelAttrs = { fontSize: String, fontColor: String }
230
- const InputLabel = styled('div', labelAttrs)`
231
- color: ${(props) =>
232
- props.theme.colors[props.fontColor]
233
- ? props.theme.colors[props.fontColor]
234
- : props.fontColor};
235
- font-size: ${(props) => props.fontSize};
236
- font-weight: 700;
237
- `
238
- const OptionalLabel = styled.span`
239
- font-weight: 300;
240
- `
241
- const inputProps = {
242
- selectWidth: String,
243
- optionWidth: String,
244
- noRelative: Boolean,
245
- }
246
- const Container = styled('div', inputProps)`
247
- width: ${(props) => props.selectWidth};
248
- position: ${(props) => (props.noRelative ? 'static' : 'relative')};
249
- display: inline-block;
250
- `
251
-
252
- const LabelWrapperAttrs = { infoTextMessage: Boolean }
253
- const LabelWrapper = styled('div', LabelWrapperAttrs)`
254
- display: inline-grid;
255
- grid-template-columns: ${(props) =>
256
- props.infoTextMessage ? 'auto auto' : 'auto'};
257
- grid-gap: 12px;
258
- align-items: center;
259
- justify-content: start;
260
- `
261
-
262
- const SelectButtonWrapperAttrs = {
263
- disabled: Boolean,
264
- }
265
- const SelectButtonWrapper = styled('div', SelectButtonWrapperAttrs)`
266
- ${(props) => (props.disabled ? 'cursor: not-allowed' : 'cursor: pointer')};
267
- `
268
-
269
- const selectButtonAttrs = {
270
- bgColor: String,
271
- fontColor: String,
272
- hasError: Boolean,
273
- disabled: Boolean,
274
- selectHeight: String,
275
- selectWidth: String,
276
- height: String,
277
- selectMinHeight: String,
278
- hasNoPadding: Boolean,
279
- showBorder: Boolean,
280
- paddingLeft: String,
281
- noRelative: Boolean,
282
- tablePaddingLeft: String,
283
- showDisabledBackground: Boolean,
284
- }
285
- const SelectButton = styled('div', selectButtonAttrs)`
286
- position: ${(props) => (props.noRelative ? 'static' : 'relative')};
287
- box-sizing: border-box;
288
- border-radius: 4px;
289
- max-width: ${(props) => (props.selectWidth ? props.selectWidth : '100%')};
290
- ${(props) =>
291
- props.isSearchBarVisible
292
- ? ''
293
- : `padding-left: ${
294
- props.hasNoPadding
295
- ? '0'
296
- : props.tablePaddingLeft
297
- ? props.tablePaddingLeft
298
- : props.paddingLeft
299
- }`};
300
- text-align: left;
301
- min-height: ${(props) =>
302
- props.selectHeight
303
- ? props.selectHeight
304
- : props.selectMinHeight
305
- ? props.selectMinHeight
306
- : props.height
307
- ? props.height
308
- : '36px'};
309
- display: flex;
310
- align-items: center;
311
- height: ${(props) => props.selectHeight};
312
- ${({ showBorder, theme, hasError }) =>
313
- showBorder &&
314
- `
315
- border: ${BORDER_WIDTH} solid ${
316
- hasError ? theme.colors.red : theme.colors.grey4
317
- }
318
- `}
319
- background-color:${(props) =>
320
- props.disabled && props.showDisabledBackground
321
- ? props.theme.colors.grey5
322
- : props.theme.colors[props.bgColor]
323
- ? props.theme.colors[props.bgColor]
324
- : props.bgColor};
325
- color: ${(props) =>
326
- props.theme.colors[props.fontColor]
327
- ? props.theme.colors[props.fontColor]
328
- : props.fontColor};
329
- ${(props) => (props.disabled ? 'pointer-events: none' : '')};
330
- overflow: hidden;
331
- & > .handle {
332
- border-right: ${(props) =>
333
- props.hasError ? props.theme.colors.red : props.theme.colors.grey4}
334
- 1px solid;
335
- }
336
- `
337
- const selectDropdownAttrs = {
338
- hoveredBgColor: String,
339
- bgColor: String,
340
- fontColor: String,
341
- optionWidth: String,
342
- hoveredIndex: Number,
343
- fontSize: String,
344
- dropdownPosition: Object,
345
- hoveredValue: Number | String,
346
- selectedValue: Number | String,
347
- noRelative: Boolean,
348
- minWidth: String,
349
- }
350
- const SelectDropdown = styled('div', selectDropdownAttrs)`
351
- box-sizing: border-box;
352
- z-index: ${(props) => (props.isActive ? '2' : '99999')};
353
- position: absolute;
354
- top: ${(props) =>
355
- props.noRelative ? 'auto' : props.dropdownPosition?.top + 'px'};
356
- left: ${(props) => props.dropdownPosition?.left}px;
357
- border: ${BORDER_WIDTH} solid ${(props) => props.theme.colors.grey4};
358
- border-radius: 4px;
359
- display: flex;
360
- flex-direction: column;
361
- align-items: flex-start;
362
- padding: 0px;
363
- box-shadow: 0px 8px 16px 0px rgba(0, 0, 0, 0.2);
364
- width: ${(props) => (props.optionWidth ? props.optionWidth : '100%')};
365
- min-width: ${(props) =>
366
- props.minWidth
367
- ? props.minWidth
368
- : props.optionWidth
369
- ? props.optionWidth
370
- : '100%'};
371
- background-color: ${(props) =>
372
- props.theme.colors[props.bgColor]
373
- ? props.theme.colors[props.bgColor]
374
- : props.bgColor};
375
- color: ${(props) =>
376
- props.theme.colors[props.fontColor]
377
- ? props.theme.colors[props.fontColor]
378
- : props.fontColor};
379
- max-height: 300px;
380
- overflow-y: auto;
381
- & > div[data-value='${(props) => props.hoveredValue}'] {
382
- background-color: ${(props) =>
383
- props.theme.colors[props.hoveredBgColor]
384
- ? props.theme.colors[props.hoveredBgColor]
385
- : props.hoveredBgColor};
386
- }
387
- font-size: ${(props) => props.fontSize};
388
- `
389
- SelectDropdown.emits = ['option-hovered', 'option-selected']
390
- const DropdownAttrs = { noRelative: Boolean }
391
- const DropdownWrapper = styled('div', DropdownAttrs)`
392
- position: ${(props) => (props.noRelative ? 'static' : 'relative')};
393
- `
394
- const inputAttrs = {
395
- alignItems: String,
396
- hasLabel: Boolean,
397
- noRelative: Boolean,
398
- }
399
- const InputWrapper = styled('div', inputAttrs)`
400
- position: ${(props) => (props.noRelative ? 'static' : 'relative')};
401
- display: grid;
402
- width: 100%;
403
- min-width: ${(props) => (props.minWidth ? props.minWidth : '150px')};
404
- align-items: center;
405
- gap: 8px;
406
- grid-template-columns: ${(props) =>
407
- props.alignItems === 'vertical' || !props.hasLabel ? '1fr' : 'auto 1fr'};
408
- `
409
-
410
- const DROPDOWN_HEIGHT_OFFSET = 4
411
- const DROPDOWN_TOP_OFFSET = 21
412
- const MIN_OPTION_LENGTH = 5
413
-
414
- const DROPDOWN_MENU_POSITIONS = {
415
- Automatic: 'automatic',
416
- Bottom: 'bottom',
417
- }
418
-
419
- export default {
420
- name: 'RCselect',
421
-
422
- components: {
423
- SelectButton,
424
- SelectButtonWrapper,
425
- SelectDropdown,
426
- Container,
427
- InputLabel,
428
- LabelWrapper,
429
- OptionalLabel,
430
- InfoText,
431
- InputWrapper,
432
- DropdownWrapper,
433
- Icon,
434
- Caret,
435
- Selector,
436
- InputText,
437
- Teleport,
438
- DraggableInputHandle,
439
- },
440
-
441
- props: {
442
- value: {
443
- required: false,
444
- default: null,
445
- },
446
- fontSize: {
447
- required: false,
448
- default: '13px',
449
- },
450
- noRelative: {
451
- required: false,
452
- default: false,
453
- },
454
- label: {
455
- required: false,
456
- },
457
- labelOptional: {
458
- required: false,
459
- default: false,
460
- },
461
- labelDataId: {
462
- required: false,
463
- default: '',
464
- },
465
- infoTextMessage: {
466
- required: false,
467
- },
468
- selectWidth: {
469
- type: String,
470
- required: false,
471
- default: '100%',
472
- },
473
- minWidth: {
474
- required: false,
475
- },
476
- maxWidth: {
477
- required: false,
478
- },
479
- selectHeight: {
480
- type: String,
481
- required: false,
482
- default: null,
483
- },
484
- height: {
485
- required: false,
486
- default: null,
487
- },
488
- selectMinHeight: {
489
- required: false,
490
- default: '36px',
491
- },
492
- optionWidth: {
493
- required: false,
494
- default: null,
495
- },
496
- hoverDropdown: {
497
- required: false,
498
- default: false,
499
- },
500
- dropdownAutoClose: {
501
- required: false,
502
- default: false,
503
- },
504
- alignItems: {
505
- required: false,
506
- default: 'horizontal',
507
- },
508
- buttonBgColor: {
509
- required: false,
510
- },
511
- buttonFontColor: {
512
- required: false,
513
- },
514
- dropdownBgColor: {
515
- required: false,
516
- default: 'grey5',
517
- },
518
- dropdownFontColor: {
519
- required: false,
520
- },
521
- dropDownArrowVisible: {
522
- required: false,
523
- default: true,
524
- },
525
- caretColor: {
526
- required: false,
527
- },
528
- labelFontColor: {
529
- required: false,
530
- },
531
- colorMode: {
532
- required: false,
533
- default: 'light',
534
- },
535
- isSearchable: {
536
- required: false,
537
- default: true,
538
- },
539
- hasError: {
540
- required: false,
541
- default: false,
542
- },
543
- disabled: {
544
- required: false,
545
- default: false,
546
- },
547
- isAutoSearch: {
548
- required: false,
549
- default: true,
550
- },
551
- showBorder: {
552
- required: false,
553
- default: true,
554
- },
555
- infoTextSize: {
556
- required: false,
557
- default: '14px',
558
- },
559
- dataId: {
560
- type: String,
561
- default: '',
562
- },
563
- hasSelectButtonPadding: {
564
- required: false,
565
- type: Boolean,
566
- default: true,
567
- },
568
- isDraggable: {
569
- type: Boolean,
570
- default: false,
571
- },
572
- leftPadding: {
573
- type: String,
574
- default: '15px',
575
- },
576
- tablePaddingLeft: {
577
- required: false,
578
- },
579
- showDisabledBackground: {
580
- required: false,
581
- default: true,
582
- },
583
- minOptionLength: {
584
- type: Number,
585
- default: MIN_OPTION_LENGTH,
586
- },
587
- dropdownMenuPosition: {
588
- type: String,
589
- default: DROPDOWN_MENU_POSITIONS.Automatic, // options: ['automatic', bottom]
590
- },
591
- infoTextWidth: {
592
- type: String,
593
- required: false,
594
- },
595
- infoTextAlignArrow: {
596
- type: String,
597
- required: false,
598
- },
599
- },
600
-
601
- data() {
602
- return {
603
- selectedValue: null,
604
- paddingLeft: this.isDraggable ? '30px' : this.leftPadding,
605
- isDropdownOpen: false,
606
- isActive: false,
607
- textSearch: '',
608
- hoveredIndex: 0,
609
- isClickOutsideActive: false,
610
- dropdownPosition: {
611
- left: null,
612
- top: null,
613
- },
614
- dropdownWidth: null,
615
- hoveredValue: null,
616
- }
617
- },
618
- computed: {
619
- optionLength() {
620
- if (this.isDropdownOpen) {
621
- return this.$refs.dropdown.$el.childElementCount > 1
622
- ? this.$refs.dropdown.$el.childElementCount
623
- : !!this.$refs.dropdown.$el.children[0]
624
- ? this.$refs.dropdown.$el.children[0].childElementCount
625
- : 0
626
- }
627
-
628
- return 0
629
- },
630
- isSearchBarVisible() {
631
- return (
632
- this.isSearchable &&
633
- this.optionLength >= this.minOptionLength &&
634
- this.isDropdownOpen
635
- )
636
- },
637
- computedWidth() {
638
- function removePX(item) {
639
- return Number(item.replace('px', ''))
640
- }
641
-
642
- return this.selectWidth === '100%'
643
- ? '100%'
644
- : removePX(this.selectWidth) - removePX(CARET_WIDTH) + 'px'
645
- },
646
- getOptionWidth() {
647
- if (this.optionWidth) return this.optionWidth
648
-
649
- return this.dropdownWidth
650
- },
651
- isSelectDropdownShown() {
652
- return (
653
- this.isDropdownOpen &&
654
- this.dropdownPosition.left !== null &&
655
- (!this.isSearchable || this.isSearchable)
656
- )
657
- },
658
- isMobileDevice() {
659
- const userAgent =
660
- navigator.userAgent || navigator.vendor || window.opera
661
- const touchCapable =
662
- 'ontouchstart' in window ||
663
- navigator.maxTouchPoints > 0 ||
664
- navigator.msMaxTouchPoints > 0
665
-
666
- return (
667
- /Android/i.test(userAgent) ||
668
- /iPad|iPhone|iPod/.test(userAgent) ||
669
- (/Macintosh/.test(userAgent) && touchCapable) ||
670
- /windows phone/i.test(userAgent)
671
- )
672
- },
673
- },
674
- watch: {
675
- value(val) {
676
- this.selectedValue = val
677
- },
678
- async isDropdownOpen(val) {
679
- if (val) {
680
- this.$emit('on-dropdown-open')
681
- setTimeout(() => {
682
- this.isClickOutsideActive = true
683
- }, 10)
684
- await this.$nextTick()
685
- this.handleSetDropdownOffet()
686
- } else {
687
- this.dropdownPosition.left = null
688
- setTimeout(() => {
689
- this.isClickOutsideActive = false
690
- }, 10)
691
- }
692
- if (val && this.isSearchable) {
693
- this.$nextTick(() => {
694
- if (this.$refs.searchInput && !this.isMobileDevice) {
695
- this.$refs.searchInput.$el.querySelector('input').focus()
696
- }
697
- })
698
- }
699
- },
700
- },
701
- mounted() {
702
- this.observeDropdownHeight()
703
- this.observeSelectWidth()
704
- window.addEventListener('resize', this.handleSetDropdownOffet)
705
- },
706
- beforeMount() {
707
- this.selectedValue = this.value
708
- document.addEventListener('click', this.clickOutside)
709
- this.getDropdownPosition()
710
- window.removeEventListener('resize', this.handleSetDropdownOffet)
711
- if (this.dropdownResizeObserver) this.dropdownResizeObserver.disconnect()
712
- if (this.selectResizeObserver) this.selectResizeObserver.disconnect()
713
- },
714
- unmounted() {
715
- document.removeEventListener('click', this.clickOutside)
716
- },
717
- methods: {
718
- focus() {
719
- this.isActive = true
720
- },
721
- blur(e) {
722
- this.isActive = false
723
- this.$emit('blur', e)
724
- },
725
- toggleDropdown() {
726
- this.isDropdownOpen = !this.isDropdownOpen
727
- },
728
- toggleCaretDropdown() {
729
- this.isDropdownOpen = !this.isDropdownOpen
730
- },
731
- closeDropdown() {
732
- this.blur()
733
- this.clearSearch()
734
- this.isDropdownOpen = false
735
- },
736
- clearSearch() {
737
- this.textSearch = ''
738
- this.searchChange('')
739
- },
740
- optionSelected(e) {
741
- this.selectedValue = e
742
- this.closeDropdown()
743
- this.blur()
744
- this.$emit('input-change', e)
745
- },
746
- optionHovered: debounce(function (e) {
747
- this.hoveredValue = e
748
- }, 300),
749
- mouseEnterHandler() {
750
- if (this.hoverDropdown) {
751
- this.focus()
752
- this.isDropdownOpen = true
753
- }
754
- },
755
- mouseLeaveHandler() {
756
- if (this.hoverDropdown) {
757
- this.blur()
758
- }
759
- },
760
- optionLeave() {
761
- if (this.dropdownAutoClose) {
762
- this.isDropdownOpen = false
763
- }
764
- },
765
- searchChange(value) {
766
- this.textSearch = value
767
- this.$emit('search-change', value)
768
- const dropdownChildren = [...this.$refs.dropdown.$el.children]
769
- dropdownChildren.forEach((el) => {
770
- if (!el.textContent.toLowerCase().includes(value.toLowerCase())) {
771
- el.style.display = 'none'
772
-
773
- return
774
- }
775
-
776
- el.style.display = 'inherit'
777
- })
778
- },
779
- clickOutside(event) {
780
- const dropdownRef = this.$refs.dropdown
781
- // we need to prevent closing on selecting an option, because in the case of
782
- // a disabled option, we don't want to close the dropdown
783
- if (!this.isClickOutsideActive) return
784
- if (
785
- this.$refs.select.$el == event.target ||
786
- this.$refs.select.$el.contains(event.target) ||
787
- event.target.id === 'more-button' ||
788
- event.target.parentNode === dropdownRef.$el
789
- ) {
790
- return
791
- } else {
792
- this.closeDropdown()
793
- }
794
- },
795
- onKeyDown(e) {
796
- if (e.key == 'ArrowDown') {
797
- this.onArrowPress(1)
798
- } else if (e.key == 'ArrowUp') {
799
- this.onArrowPress(-1)
800
- } else if (e.key == 'Enter') {
801
- const optionHoveredComponent = [...this.$refs.dropdown.$el.children][
802
- (this.hoveredIndex - 1 + this.optionLength) % this.optionLength
803
- ]
804
- this.optionSelected(optionHoveredComponent.$el.dataset.value)
805
- }
806
- },
807
- // If some part of the dropdown menu is outside viewport of the bottom of the screen,
808
- // we need to offset it and display it at the top of the select dropdown instead
809
- async getDropdownPosition() {
810
- if (
811
- !this.$refs.dropdownWrapperRef ||
812
- !this.$refs.select ||
813
- !this.$refs.dropdown
814
- ) {
815
- return
816
- }
817
- await this.$nextTick()
818
- const isDisplayedAtBottom = await this.generateDropdownPosition()
819
- // If the dropdown menu is going to be displayed at the bottom,
820
- // we need reverify its position after a dom update (nextTick)
821
- await this.$nextTick()
822
- if (isDisplayedAtBottom) this.generateDropdownPosition()
823
- },
824
- async generateDropdownPosition() {
825
- const isDropdownNotCompletelyVisible =
826
- await this.isBottomOfDropdownOutOfViewport()
827
- const dropdownWrapperEl = this.$refs.dropdownWrapperRef.$el
828
- const selectButtonHeight = this.$refs.select.$el.clientHeight
829
- const dropdownHeight = this.$refs.dropdown.$el.clientHeight
830
- const dropdownWrapperRelativeHeight =
831
- dropdownWrapperEl.getBoundingClientRect().top +
832
- window.scrollY +
833
- DROPDOWN_HEIGHT_OFFSET
834
-
835
- const top =
836
- isDropdownNotCompletelyVisible ||
837
- (!isDropdownNotCompletelyVisible &&
838
- this.dropdownMenuPosition === DROPDOWN_MENU_POSITIONS.Bottom)
839
- ? dropdownWrapperRelativeHeight
840
- : dropdownWrapperRelativeHeight -
841
- dropdownHeight -
842
- selectButtonHeight -
843
- DROPDOWN_TOP_OFFSET
844
- const left = this.dropdownPosition.left
845
- ? this.dropdownPosition.left
846
- : dropdownWrapperEl.getBoundingClientRect().left + window.scrollX
847
-
848
- this.dropdownPosition = { left: Math.floor(left), top: Math.floor(top) }
849
-
850
- return isDropdownNotCompletelyVisible
851
- },
852
- async isBottomOfDropdownOutOfViewport() {
853
- if (
854
- !this.$refs.dropdown ||
855
- this.dropdownMenuPosition === DROPDOWN_MENU_POSITIONS.Bottom
856
- ) {
857
- return false
858
- }
859
-
860
- await this.$nextTick()
861
- const rect = this.$refs.dropdown.$el.getBoundingClientRect()
862
- const windowHeight =
863
- window.innerHeight || document.documentElement.clientHeight
864
-
865
- if (windowHeight <= 650) return true
866
-
867
- // using Math.floor because the offsets may contain decimals we are not going to consider here
868
- return Math.floor(rect.top) + Math.floor(rect.height) <= windowHeight
869
- },
870
- observeDropdownHeight() {
871
- if (!this.$refs.dropdown) return
872
- this.dropdownResizeObserver = new ResizeObserver(() => {
873
- this.$nextTick(() => this.getDropdownPosition())
874
- })
875
- this.dropdownResizeObserver.observe(this.$refs.dropdown.$el)
876
- },
877
- handleSetDropdownOffet() {
878
- if (!this.$refs.select) return
879
- this.dropdownPosition.left = Math.floor(
880
- this.$refs.select.$el.getBoundingClientRect().left
881
- )
882
- this.getDropdownWidth()
883
- },
884
- observeSelectWidth() {
885
- if (!this.$refs.select) return
886
- this.selectResizeObserver = new ResizeObserver(() =>
887
- // eslint-disable-next-line vue/valid-next-tick
888
- this.$nextTick(() => this.getDropdownWidth())
889
- )
890
- this.selectResizeObserver.observe(this.$refs.dropdown.$el)
891
- },
892
- async getDropdownWidth() {
893
- if (!this.$refs.select) return
894
- await this.$nextTick()
895
- this.dropdownWidth = `${this.$refs.select.$el.clientWidth}px`
896
- },
897
- onArrowPress(dir) {
898
- let newHoveredElem
899
- const currentHoveredElem = this.$refs.dropdown.$el.querySelector(
900
- `[data-value="${this.hoveredValue}"]`
901
- )
902
- if (currentHoveredElem) {
903
- if (dir > 0) {
904
- newHoveredElem = currentHoveredElem.nextElementSibling
905
- } else {
906
- newHoveredElem = currentHoveredElem.previousElementSibling
907
- }
908
- if (newHoveredElem) {
909
- this.hoveredValue = newHoveredElem.getAttribute('data-value')
910
- const topPos = newHoveredElem.offsetTop
911
- this.$refs.dropdown.$el.scrollTop = topPos
912
- }
913
- }
914
- },
915
- },
916
- }
917
- </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
+ :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
+ :style="{
124
+ transform: `translate(${dropdownPosition?.left}px, ${
125
+ noRelative ? 'auto' : `${dropdownPosition?.top}px`
126
+ })`,
127
+ }"
128
+ :bg-color="
129
+ dropdownBgColor || colorMode == 'dark' ? 'black' : 'white'
130
+ "
131
+ :dropdown-position="dropdownPosition"
132
+ :font-color="
133
+ dropdownFontColor || colorMode == 'dark' ? 'white' : 'black'
134
+ "
135
+ :font-size="fontSize"
136
+ :hovered-bg-color="
137
+ colorMode == 'dark' ? '#000000' : dropdownBgColor
138
+ "
139
+ :hovered-index="hoveredIndex"
140
+ :hovered-value="hoveredValue"
141
+ :is-active="isActive"
142
+ :min-width="minWidth"
143
+ :no-relative="noRelative"
144
+ :option-width="getOptionWidth"
145
+ :selected-value="selectedValue"
146
+ @mouseleave="optionLeave"
147
+ @option-hovered="optionHovered"
148
+ @option-selected="optionSelected"
149
+ >
150
+ <slot name="dropdown"></slot>
151
+ </SelectDropdown>
152
+ </Teleport>
153
+ </DropdownWrapper>
154
+ </SelectButtonWrapper>
155
+ </InputWrapper>
156
+ </Container>
157
+ </template>
158
+
159
+ <script>
160
+ //How to use it
161
+ // <Select
162
+ // hoverDropdown="true"
163
+ // selectWidth="100%"
164
+ // minWidth="220px"
165
+ // optionWidth="50%"
166
+ // label="that is a label"
167
+ // alignItems="vertical"
168
+ // label-data-id="test-label-data-id"
169
+ // data-id="test-data-id"
170
+ // :hasSelectButtonPadding="false"
171
+ // >
172
+ // <template #selector="{selectedValue}">
173
+ // value selected: {{selectedValue}}
174
+ // </template>
175
+ // <template #dropdown>
176
+ // <Option value="1">value one</Option>
177
+ // <Option value="2">value two</Option>
178
+ // <Option value="3">value three</Option>
179
+ // <Option value="4">value four</Option>
180
+ // </template>
181
+ // </Select>
182
+
183
+ import { Teleport } from 'vue'
184
+ import styled from 'vue3-styled-components'
185
+ import InfoText from '../../infoText'
186
+ import Icon from '../../icon'
187
+ import InputText from '../inputText'
188
+ import DraggableInputHandle from '../../draggableInputHandle'
189
+ import { debounce } from '../../../utils'
190
+
191
+ const CARET_WIDTH = '30px'
192
+ const BORDER_WIDTH = '1px'
193
+
194
+ const Caret = styled.div`
195
+ display: flex;
196
+ align-items: center;
197
+ justify-content: center;
198
+ width: ${CARET_WIDTH};
199
+ min-width: ${CARET_WIDTH};
200
+ height: 100%;
201
+ align-items: center;
202
+ cursor: pointer;
203
+ margin-left: auto;
204
+ `
205
+
206
+ const selectorProps = {
207
+ selectWidth: String,
208
+ paddingLeft: String,
209
+ showBorder: Boolean,
210
+ }
211
+ const Selector = styled('div', selectorProps)`
212
+ ${(props) =>
213
+ props.selectWidth === '100%'
214
+ ? 'width: 100%;'
215
+ : `width: calc(${props.selectWidth} -
216
+ (
217
+ ${CARET_WIDTH} +
218
+ ${props.paddingLeft}
219
+ ${props.showBorder ? `+ (${BORDER_WIDTH} * 2)` : ''}
220
+ )
221
+ );
222
+ white-space: nowrap;
223
+ text-overflow: ellipsis;
224
+ overflow: hidden;`}
225
+ `
226
+
227
+ const labelAttrs = { fontSize: String, fontColor: String }
228
+ const InputLabel = styled('div', labelAttrs)`
229
+ color: ${(props) =>
230
+ props.theme.colors[props.fontColor]
231
+ ? props.theme.colors[props.fontColor]
232
+ : props.fontColor};
233
+ font-size: ${(props) => props.fontSize};
234
+ font-weight: 700;
235
+ `
236
+ const OptionalLabel = styled.span`
237
+ font-weight: 300;
238
+ `
239
+ const inputProps = {
240
+ selectWidth: String,
241
+ optionWidth: String,
242
+ noRelative: Boolean,
243
+ }
244
+ const Container = styled('div', inputProps)`
245
+ width: ${(props) => props.selectWidth};
246
+ position: ${(props) => (props.noRelative ? 'static' : 'relative')};
247
+ display: inline-block;
248
+ `
249
+
250
+ const LabelWrapperAttrs = { infoTextMessage: Boolean }
251
+ const LabelWrapper = styled('div', LabelWrapperAttrs)`
252
+ display: inline-grid;
253
+ grid-template-columns: ${(props) =>
254
+ props.infoTextMessage ? 'auto auto' : 'auto'};
255
+ grid-gap: 12px;
256
+ align-items: center;
257
+ justify-content: start;
258
+ `
259
+
260
+ const SelectButtonWrapperAttrs = {
261
+ disabled: Boolean,
262
+ }
263
+ const SelectButtonWrapper = styled('div', SelectButtonWrapperAttrs)`
264
+ ${(props) => (props.disabled ? 'cursor: not-allowed' : 'cursor: pointer')};
265
+ `
266
+
267
+ const selectButtonAttrs = {
268
+ bgColor: String,
269
+ fontColor: String,
270
+ hasError: Boolean,
271
+ disabled: Boolean,
272
+ selectHeight: String,
273
+ selectWidth: String,
274
+ height: String,
275
+ selectMinHeight: String,
276
+ hasNoPadding: Boolean,
277
+ showBorder: Boolean,
278
+ paddingLeft: String,
279
+ noRelative: Boolean,
280
+ tablePaddingLeft: String,
281
+ showDisabledBackground: Boolean,
282
+ }
283
+ const SelectButton = styled('div', selectButtonAttrs)`
284
+ position: ${(props) => (props.noRelative ? 'static' : 'relative')};
285
+ box-sizing: border-box;
286
+ border-radius: 4px;
287
+ max-width: ${(props) => (props.selectWidth ? props.selectWidth : '100%')};
288
+ ${(props) =>
289
+ props.isSearchBarVisible
290
+ ? ''
291
+ : `padding-left: ${
292
+ props.hasNoPadding
293
+ ? '0'
294
+ : props.tablePaddingLeft
295
+ ? props.tablePaddingLeft
296
+ : props.paddingLeft
297
+ }`};
298
+ text-align: left;
299
+ min-height: ${(props) =>
300
+ props.selectHeight
301
+ ? props.selectHeight
302
+ : props.selectMinHeight
303
+ ? props.selectMinHeight
304
+ : props.height
305
+ ? props.height
306
+ : '36px'};
307
+ display: flex;
308
+ align-items: center;
309
+ height: ${(props) => props.selectHeight};
310
+ ${({ showBorder, theme, hasError }) =>
311
+ showBorder &&
312
+ `
313
+ border: ${BORDER_WIDTH} solid ${
314
+ hasError ? theme.colors.red : theme.colors.grey4
315
+ }
316
+ `}
317
+ background-color:${(props) =>
318
+ props.disabled && props.showDisabledBackground
319
+ ? props.theme.colors.grey5
320
+ : props.theme.colors[props.bgColor]
321
+ ? props.theme.colors[props.bgColor]
322
+ : props.bgColor};
323
+ color: ${(props) =>
324
+ props.theme.colors[props.fontColor]
325
+ ? props.theme.colors[props.fontColor]
326
+ : props.fontColor};
327
+ ${(props) => (props.disabled ? 'pointer-events: none' : '')};
328
+ overflow: hidden;
329
+ & > .handle {
330
+ border-right: ${(props) =>
331
+ props.hasError ? props.theme.colors.red : props.theme.colors.grey4}
332
+ 1px solid;
333
+ }
334
+ `
335
+ const selectDropdownAttrs = {
336
+ hoveredBgColor: String,
337
+ bgColor: String,
338
+ fontColor: String,
339
+ optionWidth: String,
340
+ hoveredIndex: Number,
341
+ fontSize: String,
342
+ dropdownPosition: Object,
343
+ hoveredValue: Number | String,
344
+ selectedValue: Number | String,
345
+ noRelative: Boolean,
346
+ minWidth: String,
347
+ }
348
+ const SelectDropdown = styled('div', selectDropdownAttrs)`
349
+ box-sizing: border-box;
350
+ z-index: ${(props) => (props.isActive ? '2' : '99999')};
351
+ position: absolute;
352
+ top: 0px;
353
+ left: 0px;
354
+ border: ${BORDER_WIDTH} solid ${(props) => props.theme.colors.grey4};
355
+ border-radius: 4px;
356
+ display: flex;
357
+ flex-direction: column;
358
+ align-items: flex-start;
359
+ padding: 0px;
360
+ box-shadow: 0px 8px 16px 0px rgba(0, 0, 0, 0.2);
361
+ width: ${(props) => (props.optionWidth ? props.optionWidth : '100%')};
362
+ min-width: ${(props) =>
363
+ props.minWidth
364
+ ? props.minWidth
365
+ : props.optionWidth
366
+ ? props.optionWidth
367
+ : '100%'};
368
+ background-color: ${(props) =>
369
+ props.theme.colors[props.bgColor]
370
+ ? props.theme.colors[props.bgColor]
371
+ : props.bgColor};
372
+ color: ${(props) =>
373
+ props.theme.colors[props.fontColor]
374
+ ? props.theme.colors[props.fontColor]
375
+ : props.fontColor};
376
+ max-height: 300px;
377
+ overflow-y: auto;
378
+ & > div[data-value='${(props) => props.hoveredValue}'] {
379
+ background-color: ${(props) =>
380
+ props.theme.colors[props.hoveredBgColor]
381
+ ? props.theme.colors[props.hoveredBgColor]
382
+ : props.hoveredBgColor};
383
+ }
384
+ font-size: ${(props) => props.fontSize};
385
+ `
386
+ SelectDropdown.emits = ['option-hovered', 'option-selected']
387
+ const DropdownAttrs = { noRelative: Boolean }
388
+ const DropdownWrapper = styled('div', DropdownAttrs)`
389
+ position: ${(props) => (props.noRelative ? 'static' : 'relative')};
390
+ `
391
+ const inputAttrs = {
392
+ alignItems: String,
393
+ hasLabel: Boolean,
394
+ noRelative: Boolean,
395
+ }
396
+ const InputWrapper = styled('div', inputAttrs)`
397
+ position: ${(props) => (props.noRelative ? 'static' : 'relative')};
398
+ display: grid;
399
+ width: 100%;
400
+ min-width: ${(props) => (props.minWidth ? props.minWidth : '150px')};
401
+ align-items: center;
402
+ gap: 8px;
403
+ grid-template-columns: ${(props) =>
404
+ props.alignItems === 'vertical' || !props.hasLabel ? '1fr' : 'auto 1fr'};
405
+ `
406
+
407
+ const DROPDOWN_HEIGHT_OFFSET = 4
408
+ const DROPDOWN_TOP_OFFSET = 21
409
+ const MIN_OPTION_LENGTH = 5
410
+
411
+ const DROPDOWN_MENU_POSITIONS = {
412
+ Automatic: 'automatic',
413
+ Bottom: 'bottom',
414
+ }
415
+
416
+ export default {
417
+ name: 'RCselect',
418
+
419
+ components: {
420
+ SelectButton,
421
+ SelectButtonWrapper,
422
+ SelectDropdown,
423
+ Container,
424
+ InputLabel,
425
+ LabelWrapper,
426
+ OptionalLabel,
427
+ InfoText,
428
+ InputWrapper,
429
+ DropdownWrapper,
430
+ Icon,
431
+ Caret,
432
+ Selector,
433
+ InputText,
434
+ Teleport,
435
+ DraggableInputHandle,
436
+ },
437
+
438
+ props: {
439
+ value: {
440
+ required: false,
441
+ default: null,
442
+ },
443
+ fontSize: {
444
+ required: false,
445
+ default: '13px',
446
+ },
447
+ noRelative: {
448
+ required: false,
449
+ default: false,
450
+ },
451
+ label: {
452
+ required: false,
453
+ },
454
+ labelOptional: {
455
+ required: false,
456
+ default: false,
457
+ },
458
+ labelDataId: {
459
+ required: false,
460
+ default: '',
461
+ },
462
+ infoTextMessage: {
463
+ required: false,
464
+ },
465
+ selectWidth: {
466
+ type: String,
467
+ required: false,
468
+ default: '100%',
469
+ },
470
+ minWidth: {
471
+ required: false,
472
+ },
473
+ maxWidth: {
474
+ required: false,
475
+ },
476
+ selectHeight: {
477
+ type: String,
478
+ required: false,
479
+ default: null,
480
+ },
481
+ height: {
482
+ required: false,
483
+ default: null,
484
+ },
485
+ selectMinHeight: {
486
+ required: false,
487
+ default: '36px',
488
+ },
489
+ optionWidth: {
490
+ required: false,
491
+ default: null,
492
+ },
493
+ hoverDropdown: {
494
+ required: false,
495
+ default: false,
496
+ },
497
+ dropdownAutoClose: {
498
+ required: false,
499
+ default: false,
500
+ },
501
+ alignItems: {
502
+ required: false,
503
+ default: 'horizontal',
504
+ },
505
+ buttonBgColor: {
506
+ required: false,
507
+ },
508
+ buttonFontColor: {
509
+ required: false,
510
+ },
511
+ dropdownBgColor: {
512
+ required: false,
513
+ default: 'grey5',
514
+ },
515
+ dropdownFontColor: {
516
+ required: false,
517
+ },
518
+ dropDownArrowVisible: {
519
+ required: false,
520
+ default: true,
521
+ },
522
+ caretColor: {
523
+ required: false,
524
+ },
525
+ labelFontColor: {
526
+ required: false,
527
+ },
528
+ colorMode: {
529
+ required: false,
530
+ default: 'light',
531
+ },
532
+ isSearchable: {
533
+ required: false,
534
+ default: true,
535
+ },
536
+ hasError: {
537
+ required: false,
538
+ default: false,
539
+ },
540
+ disabled: {
541
+ required: false,
542
+ default: false,
543
+ },
544
+ isAutoSearch: {
545
+ required: false,
546
+ default: true,
547
+ },
548
+ showBorder: {
549
+ required: false,
550
+ default: true,
551
+ },
552
+ infoTextSize: {
553
+ required: false,
554
+ default: '14px',
555
+ },
556
+ dataId: {
557
+ type: String,
558
+ default: '',
559
+ },
560
+ hasSelectButtonPadding: {
561
+ required: false,
562
+ type: Boolean,
563
+ default: true,
564
+ },
565
+ isDraggable: {
566
+ type: Boolean,
567
+ default: false,
568
+ },
569
+ leftPadding: {
570
+ type: String,
571
+ default: '15px',
572
+ },
573
+ tablePaddingLeft: {
574
+ required: false,
575
+ },
576
+ showDisabledBackground: {
577
+ required: false,
578
+ default: true,
579
+ },
580
+ minOptionLength: {
581
+ type: Number,
582
+ default: MIN_OPTION_LENGTH,
583
+ },
584
+ dropdownMenuPosition: {
585
+ type: String,
586
+ default: DROPDOWN_MENU_POSITIONS.Automatic, // options: ['automatic', bottom]
587
+ },
588
+ infoTextWidth: {
589
+ type: String,
590
+ required: false,
591
+ },
592
+ infoTextAlignArrow: {
593
+ type: String,
594
+ required: false,
595
+ },
596
+ },
597
+
598
+ data() {
599
+ return {
600
+ selectedValue: null,
601
+ paddingLeft: this.isDraggable ? '30px' : this.leftPadding,
602
+ isDropdownOpen: false,
603
+ isActive: false,
604
+ textSearch: '',
605
+ hoveredIndex: 0,
606
+ isClickOutsideActive: false,
607
+ dropdownPosition: {
608
+ left: null,
609
+ top: null,
610
+ },
611
+ dropdownWidth: null,
612
+ hoveredValue: null,
613
+ isDisplayedAtBottom: true,
614
+ selectTopPosition: 0,
615
+ selectAndDropdownDistance: 0,
616
+ animationFrameId: null,
617
+ }
618
+ },
619
+ computed: {
620
+ optionLength() {
621
+ if (this.isDropdownOpen) {
622
+ return this.$refs.dropdown.$el.childElementCount > 1
623
+ ? this.$refs.dropdown.$el.childElementCount
624
+ : !!this.$refs.dropdown.$el.children[0]
625
+ ? this.$refs.dropdown.$el.children[0].childElementCount
626
+ : 0
627
+ }
628
+
629
+ return 0
630
+ },
631
+ isSearchBarVisible() {
632
+ return (
633
+ this.isSearchable &&
634
+ this.optionLength >= this.minOptionLength &&
635
+ this.isDropdownOpen
636
+ )
637
+ },
638
+ computedWidth() {
639
+ function removePX(item) {
640
+ return Number(item.replace('px', ''))
641
+ }
642
+
643
+ return this.selectWidth === '100%'
644
+ ? '100%'
645
+ : removePX(this.selectWidth) - removePX(CARET_WIDTH) + 'px'
646
+ },
647
+ getOptionWidth() {
648
+ if (this.optionWidth) return this.optionWidth
649
+
650
+ return this.dropdownWidth
651
+ },
652
+ isSelectDropdownShown() {
653
+ return (
654
+ this.isDropdownOpen &&
655
+ this.dropdownPosition.left !== null &&
656
+ (!this.isSearchable || this.isSearchable)
657
+ )
658
+ },
659
+ isMobileDevice() {
660
+ const userAgent =
661
+ navigator.userAgent || navigator.vendor || window.opera
662
+ const touchCapable =
663
+ 'ontouchstart' in window ||
664
+ navigator.maxTouchPoints > 0 ||
665
+ navigator.msMaxTouchPoints > 0
666
+
667
+ return (
668
+ /Android/i.test(userAgent) ||
669
+ /iPad|iPhone|iPod/.test(userAgent) ||
670
+ (/Macintosh/.test(userAgent) && touchCapable) ||
671
+ /windows phone/i.test(userAgent)
672
+ )
673
+ },
674
+ },
675
+ watch: {
676
+ value(val) {
677
+ this.selectedValue = val
678
+ },
679
+ async isDropdownOpen(val) {
680
+ if (val) {
681
+ this.$emit('on-dropdown-open')
682
+ setTimeout(() => {
683
+ this.isClickOutsideActive = true
684
+ }, 10)
685
+ await this.$nextTick()
686
+ this.handleSetDropdownOffet()
687
+ this.calculateSelectTopPosition()
688
+ } else {
689
+ this.dropdownPosition.left = null
690
+ if (this.animationFrameId) {
691
+ cancelAnimationFrame(this.animationFrameId)
692
+ this.animationFrameId = null
693
+ }
694
+ setTimeout(() => {
695
+ this.isClickOutsideActive = false
696
+ }, 10)
697
+ }
698
+ if (val && this.isSearchable) {
699
+ this.$nextTick(() => {
700
+ if (this.$refs.searchInput && !this.isMobileDevice) {
701
+ this.$refs.searchInput.$el.querySelector('input').focus()
702
+ }
703
+ })
704
+ }
705
+ },
706
+ isSelectDropdownShown(isShown) {
707
+ if (!isShown) return
708
+
709
+ // Need to wait for 1ms to make sure the dropdown menu is shown in the DOM
710
+ // before getting the distance between the select and the dropdown menu
711
+ setTimeout(() => {
712
+ this.getDistanceBetweenSelectAndDropdownMenu()
713
+ }, 100)
714
+ },
715
+ selectTopPosition() {
716
+ this.dropdownPosition.top =
717
+ this.selectTopPosition +
718
+ this.$refs.select.$el.clientHeight +
719
+ this.selectAndDropdownDistance
720
+ },
721
+ },
722
+ mounted() {
723
+ this.observeDropdownHeight()
724
+ this.observeSelectWidth()
725
+ window.addEventListener('resize', this.handleSetDropdownOffet)
726
+ document.body.addEventListener('scroll', this.calculateSelectTopPosition)
727
+ },
728
+ beforeMount() {
729
+ this.selectedValue = this.value
730
+ document.addEventListener('click', this.clickOutside)
731
+ this.getDropdownPosition()
732
+ window.removeEventListener('resize', this.handleSetDropdownOffet)
733
+ if (this.dropdownResizeObserver) this.dropdownResizeObserver.disconnect()
734
+ if (this.selectResizeObserver) this.selectResizeObserver.disconnect()
735
+ document.body.removeEventListener(
736
+ 'scroll',
737
+ this.calculateSelectTopPosition
738
+ )
739
+ },
740
+ unmounted() {
741
+ document.removeEventListener('click', this.clickOutside)
742
+ },
743
+ methods: {
744
+ focus() {
745
+ this.isActive = true
746
+ },
747
+ blur(e) {
748
+ this.isActive = false
749
+ this.$emit('blur', e)
750
+ },
751
+ toggleDropdown() {
752
+ this.isDropdownOpen = !this.isDropdownOpen
753
+ },
754
+ toggleCaretDropdown() {
755
+ this.isDropdownOpen = !this.isDropdownOpen
756
+ },
757
+ closeDropdown() {
758
+ this.blur()
759
+ this.clearSearch()
760
+ this.isDropdownOpen = false
761
+ },
762
+ clearSearch() {
763
+ this.textSearch = ''
764
+ this.searchChange('')
765
+ },
766
+ optionSelected(e) {
767
+ this.selectedValue = e
768
+ this.closeDropdown()
769
+ this.blur()
770
+ this.$emit('input-change', e)
771
+ },
772
+ optionHovered: debounce(function (e) {
773
+ this.hoveredValue = e
774
+ }, 300),
775
+ mouseEnterHandler() {
776
+ if (this.hoverDropdown) {
777
+ this.focus()
778
+ this.isDropdownOpen = true
779
+ }
780
+ },
781
+ mouseLeaveHandler() {
782
+ if (this.hoverDropdown) {
783
+ this.blur()
784
+ }
785
+ },
786
+ optionLeave() {
787
+ if (this.dropdownAutoClose) {
788
+ this.isDropdownOpen = false
789
+ }
790
+ },
791
+ searchChange(value) {
792
+ this.textSearch = value
793
+ this.$emit('search-change', value)
794
+ const dropdownChildren = [...this.$refs.dropdown.$el.children]
795
+ dropdownChildren.forEach((el) => {
796
+ if (!el.textContent.toLowerCase().includes(value.toLowerCase())) {
797
+ el.style.display = 'none'
798
+
799
+ return
800
+ }
801
+
802
+ el.style.display = 'inherit'
803
+ })
804
+ },
805
+ clickOutside(event) {
806
+ const dropdownRef = this.$refs.dropdown
807
+ // we need to prevent closing on selecting an option, because in the case of
808
+ // a disabled option, we don't want to close the dropdown
809
+ if (!this.isClickOutsideActive) return
810
+ if (
811
+ this.$refs.select.$el == event.target ||
812
+ this.$refs.select.$el.contains(event.target) ||
813
+ event.target.id === 'more-button' ||
814
+ event.target.parentNode === dropdownRef.$el
815
+ ) {
816
+ return
817
+ } else {
818
+ this.closeDropdown()
819
+ }
820
+ },
821
+ onKeyDown(e) {
822
+ if (e.key == 'ArrowDown') {
823
+ this.onArrowPress(1)
824
+ } else if (e.key == 'ArrowUp') {
825
+ this.onArrowPress(-1)
826
+ } else if (e.key == 'Enter') {
827
+ const optionHoveredComponent = [...this.$refs.dropdown.$el.children][
828
+ (this.hoveredIndex - 1 + this.optionLength) % this.optionLength
829
+ ]
830
+ this.optionSelected(optionHoveredComponent.$el.dataset.value)
831
+ }
832
+ },
833
+ // If some part of the dropdown menu is outside viewport of the bottom of the screen,
834
+ // we need to offset it and display it at the top of the select dropdown instead
835
+ async getDropdownPosition() {
836
+ if (
837
+ !this.$refs.dropdownWrapperRef ||
838
+ !this.$refs.select ||
839
+ !this.$refs.dropdown
840
+ ) {
841
+ return
842
+ }
843
+ await this.$nextTick()
844
+ this.isDisplayedAtBottom = await this.generateDropdownPosition()
845
+ // If the dropdown menu is going to be displayed at the bottom,
846
+ // we need reverify its position after a dom update (nextTick)
847
+ await this.$nextTick()
848
+ if (this.isDisplayedAtBottom) this.generateDropdownPosition()
849
+ },
850
+ async generateDropdownPosition() {
851
+ const isDropdownNotCompletelyVisible =
852
+ await this.isBottomOfDropdownOutOfViewport()
853
+ const dropdownWrapperEl = this.$refs.dropdownWrapperRef.$el
854
+ const selectButtonHeight = this.$refs.select.$el.clientHeight
855
+ const dropdownHeight = this.$refs.dropdown.$el.clientHeight
856
+ const dropdownWrapperRelativeHeight =
857
+ dropdownWrapperEl.getBoundingClientRect().top +
858
+ window.scrollY +
859
+ DROPDOWN_HEIGHT_OFFSET
860
+
861
+ const top =
862
+ isDropdownNotCompletelyVisible ||
863
+ (!isDropdownNotCompletelyVisible &&
864
+ this.dropdownMenuPosition === DROPDOWN_MENU_POSITIONS.Bottom)
865
+ ? dropdownWrapperRelativeHeight
866
+ : dropdownWrapperRelativeHeight -
867
+ dropdownHeight -
868
+ selectButtonHeight -
869
+ DROPDOWN_TOP_OFFSET
870
+ const left = this.dropdownPosition.left
871
+ ? this.dropdownPosition.left
872
+ : dropdownWrapperEl.getBoundingClientRect().left + window.scrollX
873
+
874
+ this.dropdownPosition = { left: Math.floor(left), top: Math.floor(top) }
875
+
876
+ return isDropdownNotCompletelyVisible
877
+ },
878
+ async isBottomOfDropdownOutOfViewport() {
879
+ if (
880
+ !this.$refs.dropdown ||
881
+ this.dropdownMenuPosition === DROPDOWN_MENU_POSITIONS.Bottom
882
+ ) {
883
+ return false
884
+ }
885
+
886
+ await this.$nextTick()
887
+ const rect = this.$refs.dropdown.$el.getBoundingClientRect()
888
+ const windowHeight =
889
+ window.innerHeight || document.documentElement.clientHeight
890
+
891
+ if (windowHeight <= 650) return true
892
+
893
+ // using Math.floor because the offsets may contain decimals we are not going to consider here
894
+ return Math.floor(rect.top) + Math.floor(rect.height) <= windowHeight
895
+ },
896
+ observeDropdownHeight() {
897
+ if (!this.$refs.dropdown) return
898
+ this.dropdownResizeObserver = new ResizeObserver(() => {
899
+ this.$nextTick(() => this.getDropdownPosition())
900
+ })
901
+ this.dropdownResizeObserver.observe(this.$refs.dropdown.$el)
902
+ },
903
+ handleSetDropdownOffet() {
904
+ if (!this.$refs.select) return
905
+ this.dropdownPosition.left = Math.floor(
906
+ this.$refs.select.$el.getBoundingClientRect().left
907
+ )
908
+ this.getDropdownWidth()
909
+ },
910
+ observeSelectWidth() {
911
+ if (!this.$refs.select) return
912
+ this.selectResizeObserver = new ResizeObserver(() =>
913
+ // eslint-disable-next-line vue/valid-next-tick
914
+ this.$nextTick(() => this.getDropdownWidth())
915
+ )
916
+ this.selectResizeObserver.observe(this.$refs.dropdown.$el)
917
+ },
918
+ async getDropdownWidth() {
919
+ if (!this.$refs.select) return
920
+ await this.$nextTick()
921
+ this.dropdownWidth = `${this.$refs.select.$el.clientWidth}px`
922
+ },
923
+ onArrowPress(dir) {
924
+ let newHoveredElem
925
+ const currentHoveredElem = this.$refs.dropdown.$el.querySelector(
926
+ `[data-value="${this.hoveredValue}"]`
927
+ )
928
+ if (currentHoveredElem) {
929
+ if (dir > 0) {
930
+ newHoveredElem = currentHoveredElem.nextElementSibling
931
+ } else {
932
+ newHoveredElem = currentHoveredElem.previousElementSibling
933
+ }
934
+ if (newHoveredElem) {
935
+ this.hoveredValue = newHoveredElem.getAttribute('data-value')
936
+ const topPos = newHoveredElem.offsetTop
937
+ this.$refs.dropdown.$el.scrollTop = topPos
938
+ }
939
+ }
940
+ },
941
+ getDistanceBetweenSelectAndDropdownMenu() {
942
+ const wholeSelectTopPosition =
943
+ this.selectTopPosition + this.$refs.select.$el.clientHeight
944
+ this.selectAndDropdownDistance =
945
+ this.dropdownPosition.top - wholeSelectTopPosition
946
+ },
947
+ calculateSelectTopPosition() {
948
+ const selectRef = this.$refs.select
949
+ if (selectRef) {
950
+ const currentTopPosition =
951
+ selectRef.$el.getBoundingClientRect().top + window.scrollY
952
+ if (this.selectTopPosition !== currentTopPosition) {
953
+ this.selectTopPosition = currentTopPosition
954
+ }
955
+ }
956
+ this.animationFrameId = requestAnimationFrame(
957
+ this.calculateSelectTopPosition
958
+ )
959
+ },
960
+ },
961
+ }
962
+ </script>