@eturnity/eturnity_reusable_components 7.24.3-EPDM-8441.0 → 7.24.3-EPDM-11143.1

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