@eturnity/eturnity_reusable_components 7.32.1-EPDM-11313.0 → 7.32.1-EPDM-11143.0

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