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