@milaboratories/uikit 2.1.2 → 2.2.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 (274) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/dist/pl-uikit.js +3118 -2901
  3. package/dist/pl-uikit.umd.cjs +10 -10
  4. package/dist/src/components/PlDropdownLegacy/PlDropdownLegacy.vue.d.ts +78 -0
  5. package/dist/src/components/PlDropdownLegacy/__tests__/PlDropdownLegacy.spec.d.ts +1 -0
  6. package/dist/src/components/PlDropdownLegacy/index.d.ts +1 -0
  7. package/dist/src/components/PlDropdownRef/PlDropdownRef.vue.d.ts +2 -1
  8. package/dist/src/components/PlTooltip/PlTooltip.vue.d.ts +1 -1
  9. package/dist/src/composition/useEventListener.d.ts +1 -1
  10. package/dist/src/generated/icons-16.d.ts +1 -1
  11. package/dist/src/generated/icons-24.d.ts +1 -1
  12. package/dist/src/index.d.ts +28 -59
  13. package/dist/style.css +1 -1
  14. package/dist/tsconfig.lib.tsbuildinfo +1 -1
  15. package/package.json +1 -1
  16. package/src/assets/base.scss +4 -0
  17. package/src/assets/icons/icon-assets/16_calendar.svg +7 -0
  18. package/src/assets/icons/icon-assets/16_caret-down.svg +3 -0
  19. package/src/assets/icons/icon-assets/16_caret-left.svg +3 -0
  20. package/src/assets/icons/icon-assets/16_caret-right.svg +3 -0
  21. package/src/assets/icons/icon-assets/16_caret-up.svg +3 -0
  22. package/src/assets/icons/icon-assets/16_clipboard-copied.svg +1 -3
  23. package/src/assets/icons/icon-assets/16_clipboard.svg +1 -11
  24. package/src/assets/icons/icon-assets/16_delete-bin.svg +2 -6
  25. package/src/assets/icons/icon-assets/16_delete-circle.svg +1 -2
  26. package/src/assets/icons/icon-assets/16_error.svg +1 -2
  27. package/src/assets/icons/icon-assets/16_help-outline.svg +1 -1
  28. package/src/assets/icons/icon-assets/16_link.svg +1 -2
  29. package/src/assets/icons/icon-assets/16_paper-clip.svg +3 -0
  30. package/src/assets/icons/icon-assets/16_renew.svg +3 -0
  31. package/src/assets/icons/icon-assets/16_restart.svg +1 -1
  32. package/src/assets/icons/icon-assets/16_settings.svg +1 -1
  33. package/src/assets/icons/icon-assets/16_stop.svg +1 -1
  34. package/src/assets/icons/icon-assets/16_success.svg +1 -2
  35. package/src/assets/icons/icon-assets/16_time.svg +10 -0
  36. package/src/assets/icons/icon-assets/16_zip.svg +1 -2
  37. package/src/assets/icons/icon-assets/24_annotate.svg +2 -2
  38. package/src/assets/icons/icon-assets/24_annotation.svg +6 -6
  39. package/src/assets/icons/icon-assets/24_calendar.svg +6 -0
  40. package/src/assets/icons/icon-assets/24_clipboard-copied.svg +1 -3
  41. package/src/assets/icons/icon-assets/24_clipboard.svg +2 -2
  42. package/src/assets/icons/icon-assets/24_cluster.svg +1 -1
  43. package/src/assets/icons/icon-assets/24_container.svg +7 -0
  44. package/src/assets/icons/icon-assets/24_cookie.svg +1 -1
  45. package/src/assets/icons/icon-assets/24_copy.svg +1 -1
  46. package/src/assets/icons/icon-assets/24_cpu.svg +1 -1
  47. package/src/assets/icons/icon-assets/24_debug.svg +1 -1
  48. package/src/assets/icons/icon-assets/24_file-doc-add.svg +4 -0
  49. package/src/assets/icons/icon-assets/24_file-doc-download.svg +4 -0
  50. package/src/assets/icons/icon-assets/24_file-doc-import.svg +4 -0
  51. package/src/assets/icons/icon-assets/24_file-doc.svg +5 -0
  52. package/src/assets/icons/icon-assets/24_fire-tips.svg +1 -1
  53. package/src/assets/icons/icon-assets/24_folder-parent.svg +1 -1
  54. package/src/assets/icons/icon-assets/24_generate.svg +1 -1
  55. package/src/assets/icons/icon-assets/24_heatmap.svg +10 -9
  56. package/src/assets/icons/icon-assets/24_link.svg +2 -2
  57. package/src/assets/icons/icon-assets/24_paper-clip.svg +1 -1
  58. package/src/assets/icons/icon-assets/24_pin.svg +1 -1
  59. package/src/assets/icons/icon-assets/24_product.svg +3 -0
  60. package/src/assets/icons/icon-assets/24_renew.svg +3 -0
  61. package/src/assets/icons/icon-assets/24_restart.svg +1 -1
  62. package/src/assets/icons/icon-assets/24_slice.svg +2 -2
  63. package/src/assets/icons/icon-assets/24_social-github.svg +1 -1
  64. package/src/assets/icons/icon-assets/24_social-twitter-X.svg +1 -1
  65. package/src/assets/icons/icon-assets/24_social-twitter-bird.svg +1 -1
  66. package/src/assets/icons/icon-assets/24_stop.svg +1 -1
  67. package/src/assets/icons/icon-assets/24_table-add.svg +3 -0
  68. package/src/assets/icons/icon-assets/24_table-alias.svg +3 -0
  69. package/src/assets/icons/icon-assets/24_table-import.svg +3 -0
  70. package/src/assets/icons/icon-assets/24_table.svg +1 -2
  71. package/src/assets/icons/icon-assets/24_theme-dark.svg +3 -0
  72. package/src/assets/icons/icon-assets/24_theme-light.svg +3 -0
  73. package/src/assets/icons/icon-assets/24_time.svg +3 -0
  74. package/src/assets/icons/icon-assets/24_view-hide.svg +3 -0
  75. package/src/assets/icons/icon-assets/24_view-show.svg +3 -0
  76. package/src/assets/icons/icon-assets/24_warning.svg +1 -3
  77. package/src/assets/icons/icon-assets/24_wetlab.svg +1 -1
  78. package/src/assets/icons/icon-assets/24_zip.svg +1 -2
  79. package/src/assets/icons/icon-assets-min/16_calendar.svg +1 -0
  80. package/src/assets/icons/icon-assets-min/16_caret-down.svg +1 -0
  81. package/src/assets/icons/icon-assets-min/16_caret-left.svg +1 -0
  82. package/src/assets/icons/icon-assets-min/16_caret-right.svg +1 -0
  83. package/src/assets/icons/icon-assets-min/16_caret-up.svg +1 -0
  84. package/src/assets/icons/icon-assets-min/16_clipboard-copied.svg +1 -1
  85. package/src/assets/icons/icon-assets-min/16_clipboard.svg +1 -1
  86. package/src/assets/icons/icon-assets-min/16_delete-bin.svg +1 -1
  87. package/src/assets/icons/icon-assets-min/16_delete-circle.svg +1 -1
  88. package/src/assets/icons/icon-assets-min/16_download.svg +1 -0
  89. package/src/assets/icons/icon-assets-min/16_error.svg +1 -1
  90. package/src/assets/icons/icon-assets-min/16_link.svg +1 -1
  91. package/src/assets/icons/icon-assets-min/16_paper-clip.svg +1 -0
  92. package/src/assets/icons/icon-assets-min/16_renew.svg +1 -0
  93. package/src/assets/icons/icon-assets-min/16_restart.svg +1 -1
  94. package/src/assets/icons/icon-assets-min/16_stop.svg +1 -1
  95. package/src/assets/icons/icon-assets-min/16_success.svg +1 -1
  96. package/src/assets/icons/icon-assets-min/16_time.svg +1 -0
  97. package/src/assets/icons/icon-assets-min/16_zip.svg +1 -1
  98. package/src/assets/icons/icon-assets-min/24_annotate.svg +1 -1
  99. package/src/assets/icons/icon-assets-min/24_annotation.svg +1 -1
  100. package/src/assets/icons/icon-assets-min/24_calendar.svg +1 -0
  101. package/src/assets/icons/icon-assets-min/24_clipboard-copied.svg +1 -1
  102. package/src/assets/icons/icon-assets-min/24_clipboard.svg +1 -1
  103. package/src/assets/icons/icon-assets-min/24_cluster.svg +1 -1
  104. package/src/assets/icons/icon-assets-min/24_container.svg +1 -0
  105. package/src/assets/icons/icon-assets-min/24_cookie.svg +1 -1
  106. package/src/assets/icons/icon-assets-min/24_copy.svg +1 -1
  107. package/src/assets/icons/icon-assets-min/24_cpu.svg +1 -1
  108. package/src/assets/icons/icon-assets-min/24_debug.svg +1 -1
  109. package/src/assets/icons/icon-assets-min/24_download.svg +1 -0
  110. package/src/assets/icons/icon-assets-min/24_file-doc-add.svg +1 -0
  111. package/src/assets/icons/icon-assets-min/24_file-doc-download.svg +1 -0
  112. package/src/assets/icons/icon-assets-min/24_file-doc-import.svg +1 -0
  113. package/src/assets/icons/icon-assets-min/24_file-doc.svg +1 -0
  114. package/src/assets/icons/icon-assets-min/24_fire-tips.svg +1 -1
  115. package/src/assets/icons/icon-assets-min/24_folder-parent.svg +1 -1
  116. package/src/assets/icons/icon-assets-min/24_generate.svg +1 -1
  117. package/src/assets/icons/icon-assets-min/24_heatmap.svg +1 -1
  118. package/src/assets/icons/icon-assets-min/24_link.svg +1 -1
  119. package/src/assets/icons/icon-assets-min/24_paper-clip.svg +1 -1
  120. package/src/assets/icons/icon-assets-min/24_pin.svg +1 -1
  121. package/src/assets/icons/icon-assets-min/24_product.svg +1 -0
  122. package/src/assets/icons/icon-assets-min/24_renew.svg +1 -0
  123. package/src/assets/icons/icon-assets-min/24_restart.svg +1 -1
  124. package/src/assets/icons/icon-assets-min/24_slice.svg +1 -1
  125. package/src/assets/icons/icon-assets-min/24_social-github.svg +1 -1
  126. package/src/assets/icons/icon-assets-min/24_social-twitter-X.svg +1 -1
  127. package/src/assets/icons/icon-assets-min/24_social-twitter-bird.svg +1 -1
  128. package/src/assets/icons/icon-assets-min/24_stop.svg +1 -1
  129. package/src/assets/icons/icon-assets-min/24_table-add.svg +1 -0
  130. package/src/assets/icons/icon-assets-min/24_table-alias.svg +1 -0
  131. package/src/assets/icons/icon-assets-min/24_table-import.svg +1 -0
  132. package/src/assets/icons/icon-assets-min/24_table.svg +1 -1
  133. package/src/assets/icons/icon-assets-min/24_theme-dark.svg +1 -0
  134. package/src/assets/icons/icon-assets-min/24_theme-light.svg +1 -0
  135. package/src/assets/icons/icon-assets-min/24_time.svg +1 -0
  136. package/src/assets/icons/icon-assets-min/24_view-hide.svg +1 -0
  137. package/src/assets/icons/icon-assets-min/24_view-show.svg +1 -0
  138. package/src/assets/icons/icon-assets-min/24_warning.svg +1 -1
  139. package/src/assets/icons/icon-assets-min/24_wetlab.svg +1 -1
  140. package/src/assets/icons/icon-assets-min/24_zip.svg +1 -1
  141. package/src/assets/icons/icons-16-generated.json +9 -11
  142. package/src/assets/icons/icons-16-generated.scss +9 -11
  143. package/src/assets/icons/icons-24-generated.json +18 -48
  144. package/src/assets/icons/icons-24-generated.scss +18 -48
  145. package/src/assets/variables.scss +3 -1
  146. package/src/components/PlCheckbox/pl-checkbox.scss +2 -0
  147. package/src/components/PlChip/PlChip.vue +5 -5
  148. package/src/components/PlChip/pl-chip.scss +2 -2
  149. package/src/components/PlDropdown/PlDropdown.vue +61 -24
  150. package/src/components/PlDropdown/__tests__/PlDropdown.spec.ts +7 -6
  151. package/src/components/PlDropdown/pl-dropdown.scss +65 -67
  152. package/src/components/PlDropdownLegacy/PlDropdownLegacy.vue +372 -0
  153. package/src/components/PlDropdownLegacy/__tests__/PlDropdownLegacy.spec.ts +33 -0
  154. package/src/components/PlDropdownLegacy/index.ts +1 -0
  155. package/src/components/PlDropdownLegacy/pl-dropdown-legacy.scss +260 -0
  156. package/src/components/PlDropdownLine/PlDropdownLine.vue +81 -42
  157. package/src/components/PlDropdownLine/pl-dropdown-line.scss +5 -5
  158. package/src/components/PlDropdownMulti/PlDropdownMulti.vue +62 -27
  159. package/src/components/PlDropdownMulti/__tests__/PlDropdownMulti.spec.ts +12 -7
  160. package/src/components/PlDropdownMulti/pl-dropdown-multi.scss +11 -8
  161. package/src/components/PlDropdownRef/PlDropdownRef.vue +1 -0
  162. package/src/components/PlDropdownRef/__tests__/PlDropdownRef.spec.ts +11 -8
  163. package/src/components/PlFileInput/PlFileInput.vue +1 -1
  164. package/src/components/PlTextField/PlTextField.vue +1 -1
  165. package/src/composition/useEventListener.ts +3 -3
  166. package/src/composition/usePosition.ts +2 -2
  167. package/src/generated/icons-16.ts +9 -11
  168. package/src/generated/icons-24.ts +18 -48
  169. package/src/index.ts +1 -0
  170. package/src/assets/icons/icon-assets/16_clear.svg +0 -10
  171. package/src/assets/icons/icon-assets/16_comp.svg +0 -3
  172. package/src/assets/icons/icon-assets/16_compare.svg +0 -4
  173. package/src/assets/icons/icon-assets/16_delete.svg +0 -4
  174. package/src/assets/icons/icon-assets/16_filter-2.svg +0 -4
  175. package/src/assets/icons/icon-assets/16_link-arrow.svg +0 -3
  176. package/src/assets/icons/icon-assets/16_more-horizontal.svg +0 -5
  177. package/src/assets/icons/icon-assets/16_required.svg +0 -3
  178. package/src/assets/icons/icon-assets/16_settings-2.svg +0 -4
  179. package/src/assets/icons/icon-assets/16_sorter.svg +0 -9
  180. package/src/assets/icons/icon-assets/24_box-dot.svg +0 -6
  181. package/src/assets/icons/icon-assets/24_boxplot-1.svg +0 -5
  182. package/src/assets/icons/icon-assets/24_cloud-down.svg +0 -4
  183. package/src/assets/icons/icon-assets/24_cloud-up.svg +0 -4
  184. package/src/assets/icons/icon-assets/24_code-2.svg +0 -5
  185. package/src/assets/icons/icon-assets/24_cross-bars.svg +0 -13
  186. package/src/assets/icons/icon-assets/24_dark-mode.svg +0 -5
  187. package/src/assets/icons/icon-assets/24_delete.svg +0 -6
  188. package/src/assets/icons/icon-assets/24_dendrogram-X-1.svg +0 -8
  189. package/src/assets/icons/icon-assets/24_dendrogram-Y-1.svg +0 -8
  190. package/src/assets/icons/icon-assets/24_download-files.svg +0 -6
  191. package/src/assets/icons/icon-assets/24_export-2.svg +0 -4
  192. package/src/assets/icons/icon-assets/24_external-link.svg +0 -3
  193. package/src/assets/icons/icon-assets/24_fill-color.svg +0 -3
  194. package/src/assets/icons/icon-assets/24_filters-gate.svg +0 -11
  195. package/src/assets/icons/icon-assets/24_frame-type.svg +0 -11
  196. package/src/assets/icons/icon-assets/24_hide.svg +0 -5
  197. package/src/assets/icons/icon-assets/24_import-2.svg +0 -4
  198. package/src/assets/icons/icon-assets/24_import-download.svg +0 -4
  199. package/src/assets/icons/icon-assets/24_info-circle-outline.svg +0 -5
  200. package/src/assets/icons/icon-assets/24_light-mode.svg +0 -18
  201. package/src/assets/icons/icon-assets/24_linetype.svg +0 -13
  202. package/src/assets/icons/icon-assets/24_local.svg +0 -5
  203. package/src/assets/icons/icon-assets/24_logout.svg +0 -4
  204. package/src/assets/icons/icon-assets/24_mode-dark.svg +0 -5
  205. package/src/assets/icons/icon-assets/24_mode-light.svg +0 -18
  206. package/src/assets/icons/icon-assets/24_more-horizontal.svg +0 -5
  207. package/src/assets/icons/icon-assets/24_outlier-shape.svg +0 -28
  208. package/src/assets/icons/icon-assets/24_position.svg +0 -28
  209. package/src/assets/icons/icon-assets/24_progress-2.svg +0 -9
  210. package/src/assets/icons/icon-assets/24_progress.svg +0 -10
  211. package/src/assets/icons/icon-assets/24_radio-btn.svg +0 -29
  212. package/src/assets/icons/icon-assets/24_rotation.svg +0 -11
  213. package/src/assets/icons/icon-assets/24_save.svg +0 -3
  214. package/src/assets/icons/icon-assets/24_server-2.svg +0 -10
  215. package/src/assets/icons/icon-assets/24_settings-2.svg +0 -4
  216. package/src/assets/icons/icon-assets/24_show.svg +0 -4
  217. package/src/assets/icons/icon-assets/24_social-media.svg +0 -16
  218. package/src/assets/icons/icon-assets/24_stacked-bar.svg +0 -17
  219. package/src/assets/icons/icon-assets/24_stroke-non.svg +0 -6
  220. package/src/assets/icons/icon-assets/24_stroke.svg +0 -3
  221. package/src/assets/icons/icon-assets/24_table-upload.svg +0 -4
  222. package/src/assets/icons/icon-assets/24_title-position.svg +0 -15
  223. package/src/assets/icons/icon-assets/24_upload-files.svg +0 -6
  224. package/src/assets/icons/icon-assets/24_view-off.svg +0 -5
  225. package/src/assets/icons/icon-assets/24_view-on.svg +0 -4
  226. package/src/assets/icons/icon-assets-min/16_comp.svg +0 -1
  227. package/src/assets/icons/icon-assets-min/16_delete.svg +0 -1
  228. package/src/assets/icons/icon-assets-min/16_filter-2.svg +0 -1
  229. package/src/assets/icons/icon-assets-min/16_more-horizontal.svg +0 -1
  230. package/src/assets/icons/icon-assets-min/16_settings-2.svg +0 -1
  231. package/src/assets/icons/icon-assets-min/24_box-dot.svg +0 -1
  232. package/src/assets/icons/icon-assets-min/24_boxplot-1.svg +0 -1
  233. package/src/assets/icons/icon-assets-min/24_cloud-down.svg +0 -1
  234. package/src/assets/icons/icon-assets-min/24_cloud-up.svg +0 -1
  235. package/src/assets/icons/icon-assets-min/24_code-2.svg +0 -1
  236. package/src/assets/icons/icon-assets-min/24_cross-bars.svg +0 -1
  237. package/src/assets/icons/icon-assets-min/24_dark-mode.svg +0 -1
  238. package/src/assets/icons/icon-assets-min/24_delete.svg +0 -1
  239. package/src/assets/icons/icon-assets-min/24_dendrogram-X-1.svg +0 -1
  240. package/src/assets/icons/icon-assets-min/24_dendrogram-Y-1.svg +0 -1
  241. package/src/assets/icons/icon-assets-min/24_download-files.svg +0 -1
  242. package/src/assets/icons/icon-assets-min/24_fill-color.svg +0 -1
  243. package/src/assets/icons/icon-assets-min/24_filters-gate.svg +0 -1
  244. package/src/assets/icons/icon-assets-min/24_frame-type.svg +0 -1
  245. package/src/assets/icons/icon-assets-min/24_import-2.svg +0 -1
  246. package/src/assets/icons/icon-assets-min/24_import-download.svg +0 -1
  247. package/src/assets/icons/icon-assets-min/24_info-circle-outline.svg +0 -1
  248. package/src/assets/icons/icon-assets-min/24_light-mode.svg +0 -1
  249. package/src/assets/icons/icon-assets-min/24_linetype.svg +0 -1
  250. package/src/assets/icons/icon-assets-min/24_local.svg +0 -1
  251. package/src/assets/icons/icon-assets-min/24_logout.svg +0 -1
  252. package/src/assets/icons/icon-assets-min/24_mode-dark.svg +0 -1
  253. package/src/assets/icons/icon-assets-min/24_mode-light.svg +0 -1
  254. package/src/assets/icons/icon-assets-min/24_more-horizontal.svg +0 -1
  255. package/src/assets/icons/icon-assets-min/24_outlier-shape.svg +0 -1
  256. package/src/assets/icons/icon-assets-min/24_position.svg +0 -1
  257. package/src/assets/icons/icon-assets-min/24_progress-2.svg +0 -1
  258. package/src/assets/icons/icon-assets-min/24_progress.svg +0 -1
  259. package/src/assets/icons/icon-assets-min/24_radio-btn.svg +0 -1
  260. package/src/assets/icons/icon-assets-min/24_rotation.svg +0 -1
  261. package/src/assets/icons/icon-assets-min/24_save.svg +0 -1
  262. package/src/assets/icons/icon-assets-min/24_server-2.svg +0 -1
  263. package/src/assets/icons/icon-assets-min/24_settings-2.svg +0 -1
  264. package/src/assets/icons/icon-assets-min/24_social-media.svg +0 -1
  265. package/src/assets/icons/icon-assets-min/24_stacked-bar.svg +0 -1
  266. package/src/assets/icons/icon-assets-min/24_stroke-non.svg +0 -1
  267. package/src/assets/icons/icon-assets-min/24_stroke.svg +0 -1
  268. package/src/assets/icons/icon-assets-min/24_upload-files.svg +0 -1
  269. package/src/assets/icons/icon-assets-min/24_view-off.svg +0 -1
  270. package/src/assets/icons/icon-assets-min/24_view-on.svg +0 -1
  271. /package/src/assets/icons/icon-assets/{16_import.svg → 16_download.svg} +0 -0
  272. /package/src/assets/icons/icon-assets/{24_import.svg → 24_download.svg} +0 -0
  273. /package/src/assets/icons/icon-assets/{24_frame-type--left.svg → 24_frame-type-left.svg} +0 -0
  274. /package/src/assets/icons/icon-assets-min/{24_frame-type--left.svg → 24_frame-type-left.svg} +0 -0
@@ -12,6 +12,7 @@ import DropdownListItem from '@/components/DropdownListItem.vue';
12
12
  import TabItem from '@/components/TabItem.vue';
13
13
  import type { ListOption } from '@/types';
14
14
  import { normalizeListOptions } from '@/helpers/utils';
15
+ import { useElementPosition } from '@/composition/usePosition';
15
16
 
16
17
  const emit = defineEmits(['update:modelValue']); // at the top always
17
18
 
@@ -38,6 +39,7 @@ const props = withDefaults(
38
39
  const data = reactive({
39
40
  isOpen: false,
40
41
  activeOption: -1,
42
+ optionsHeight: 0,
41
43
  });
42
44
 
43
45
  const container = ref<HTMLElement>();
@@ -85,7 +87,7 @@ const placeholderVal = computed(() => {
85
87
  }
86
88
  }
87
89
 
88
- return modelText.value || '...';
90
+ return modelText.value ?? '...';
89
91
  });
90
92
 
91
93
  useClickOutside(container, () => {
@@ -169,13 +171,14 @@ function isItemSelected(item: ListOption): boolean {
169
171
  return deepEqual(item.value, props.modelValue);
170
172
  }
171
173
 
172
- function onBlur(event: Event) {
173
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
174
- if (!container?.value?.contains((event as any).relatedTarget)) {
175
- data.isOpen = false;
174
+ const onFocusOut = (event: FocusEvent) => {
175
+ const relatedTarget = event.relatedTarget as Node | null;
176
+
177
+ if (!container.value?.contains(relatedTarget) && !list.value?.contains(relatedTarget)) {
176
178
  searchPhrase.value = '';
179
+ data.isOpen = false;
177
180
  }
178
- }
181
+ };
179
182
 
180
183
  function handleKeydown(e: { code: string; preventDefault(): void }) {
181
184
  const { activeOption } = data;
@@ -223,6 +226,33 @@ function scrollIntoActive() {
223
226
  function clearModel() {
224
227
  emit('update:modelValue', undefined);
225
228
  }
229
+
230
+ const optionsStyle = reactive({
231
+ top: '0px',
232
+ left: '0px',
233
+ });
234
+
235
+ watch(list, (el) => {
236
+ if (el) {
237
+ const rect = el.getBoundingClientRect();
238
+ data.optionsHeight = rect.height;
239
+ window.dispatchEvent(new CustomEvent('adjust'));
240
+ }
241
+ });
242
+
243
+ useElementPosition(container, (pos) => {
244
+ const gap = 2;
245
+
246
+ const downTopOffset = pos.top + pos.height + gap;
247
+
248
+ if (downTopOffset + data.optionsHeight > pos.clientHeight) {
249
+ optionsStyle.top = pos.top - data.optionsHeight - gap + 'px';
250
+ } else {
251
+ optionsStyle.top = downTopOffset + 'px';
252
+ }
253
+
254
+ optionsStyle.left = pos.left + 'px';
255
+ });
226
256
  </script>
227
257
 
228
258
  <template>
@@ -231,53 +261,62 @@ function clearModel() {
231
261
  ref="container"
232
262
  tabindex="0"
233
263
  :class="classes"
234
- class="ui-line-dropdown uc-pointer"
264
+ class="pl-line-dropdown uc-pointer"
235
265
  @keydown="handleKeydown"
236
- @focusout="onBlur"
266
+ @focusout="onFocusOut"
237
267
  @click="toggleList"
238
268
  >
239
- <div class="ui-line-dropdown__prefix">{{ props?.prefix }}</div>
269
+ <div class="pl-line-dropdown__prefix">{{ props?.prefix }}</div>
240
270
 
241
- <ResizableInput v-model="inputModel" :placeholder="placeholderVal" :disabled="props.disabled" class="ui-line-dropdown__input" />
271
+ <ResizableInput v-model="inputModel" :placeholder="placeholderVal" :disabled="props.disabled" class="pl-line-dropdown__input" />
242
272
 
243
- <div class="ui-line-dropdown__icon-wrapper">
244
- <div v-show="!canShowClearBtn" class="ui-line-dropdown__icon" />
245
- <div v-show="canShowClearBtn" class="ui-line-dropdown__icon-clear" @click="clearModel" />
273
+ <div class="pl-line-dropdown__icon-wrapper">
274
+ <div v-show="!canShowClearBtn" class="pl-line-dropdown__icon" />
275
+ <div v-show="canShowClearBtn" class="pl-line-dropdown__icon-clear" @click="clearModel" />
246
276
  </div>
247
- <div v-if="props.mode === 'list'" v-show="data.isOpen" ref="list" class="ui-line-dropdown__items">
248
- <template v-for="(item, index) in options" :key="index">
249
- <slot
250
- name="item"
251
- :item="item"
252
- :text-item="'text'"
253
- :is-selected="isItemSelected(item)"
254
- :is-hovered="data.activeOption == index"
255
- @click.stop="selectItem(item)"
256
- >
257
- <DropdownListItem
258
- :option="item"
277
+ <Teleport v-if="data.isOpen" to="body">
278
+ <div v-if="props.mode === 'list'" ref="list" :style="optionsStyle" tabindex="-1" class="pl-line-dropdown__items" @focusout="onFocusOut">
279
+ <template v-for="(item, index) in options" :key="index">
280
+ <slot
281
+ name="item"
282
+ :item="item"
259
283
  :text-item="'text'"
260
284
  :is-selected="isItemSelected(item)"
261
285
  :is-hovered="data.activeOption == index"
262
- size="medium"
263
286
  @click.stop="selectItem(item)"
264
- />
265
- </slot>
266
- </template>
267
-
268
- <div v-if="options.length === 0" class="ui-line-dropdown__no-item">
269
- <div class="ui-line-dropdown__no-item-title text-s">Didn't find anything that matched</div>
287
+ >
288
+ <DropdownListItem
289
+ :option="item"
290
+ :text-item="'text'"
291
+ :is-selected="isItemSelected(item)"
292
+ :is-hovered="data.activeOption == index"
293
+ size="medium"
294
+ @click.stop="selectItem(item)"
295
+ />
296
+ </slot>
297
+ </template>
298
+
299
+ <div v-if="options.length === 0" class="pl-line-dropdown__no-item">
300
+ <div class="pl-line-dropdown__no-item-title text-s">Didn't find anything that matched</div>
301
+ </div>
270
302
  </div>
271
- </div>
272
- <div v-if="props.mode === 'tabs'" v-show="data.isOpen" ref="list" :style="props.tabsContainerStyles" class="ui-line-dropdown__items-tabs">
273
- <template v-for="(item, index) in options" :key="index">
274
- <slot name="item" :item="item" :is-selected="isItemSelected(item)" :is-hovered="data.activeOption == index" @click.stop="selectItem(item)">
275
- <TabItem :option="item" :is-selected="isItemSelected(item)" :is-hovered="data.activeOption == index" @click.stop="selectItem(item)" />
276
- </slot>
277
- </template>
278
- <div v-if="options.length === 0" class="ui-line-dropdown__no-item">
279
- <div class="ui-line-dropdown__no-item-title text-s">Didn't find anything that matched</div>
303
+ <div
304
+ v-else-if="props.mode === 'tabs'"
305
+ ref="list"
306
+ :style="optionsStyle"
307
+ tabindex="-1"
308
+ class="pl-line-dropdown__items-tabs"
309
+ @focusout="onFocusOut"
310
+ >
311
+ <template v-for="(item, index) in options" :key="index">
312
+ <slot name="item" :item="item" :is-selected="isItemSelected(item)" :is-hovered="data.activeOption == index" @click.stop="selectItem(item)">
313
+ <TabItem :option="item" :is-selected="isItemSelected(item)" :is-hovered="data.activeOption == index" @click.stop="selectItem(item)" />
314
+ </slot>
315
+ </template>
316
+ <div v-if="options.length === 0" class="pl-line-dropdown__no-item">
317
+ <div class="pl-line-dropdown__no-item-title text-s">Didn't find anything that matched</div>
318
+ </div>
280
319
  </div>
281
- </div>
320
+ </Teleport>
282
321
  </div>
283
322
  </template>
@@ -1,6 +1,6 @@
1
1
  @import "@/assets/mixins";
2
2
 
3
- .ui-line-dropdown {
3
+ .pl-line-dropdown {
4
4
  display: flex;
5
5
  align-items: center;
6
6
  width: fit-content;
@@ -113,8 +113,8 @@
113
113
 
114
114
  &__items {
115
115
  position: absolute;
116
- top: 110%;
117
- z-index: 1;
116
+ top: 0;
117
+ z-index: var(--z-dropdown-options);
118
118
  border-radius: 6px;
119
119
  padding: 12px 0;
120
120
  border: 1px solid var(--color-div-grey);
@@ -130,8 +130,8 @@
130
130
  &__items-tabs {
131
131
  display: flex;
132
132
  position: absolute;
133
- top: 110%;
134
- z-index: 1;
133
+ top: 0;
134
+ z-index: var(--z-dropdown-options);
135
135
  background-color: var(--color-div-bw);
136
136
  overflow-x: scroll;
137
137
  max-width: 400px;
@@ -20,6 +20,7 @@ import { scrollIntoView } from '@/helpers/dom';
20
20
  import DropdownListItem from '@/components/DropdownListItem.vue';
21
21
  import { deepEqual, deepIncludes } from '@/helpers/objects';
22
22
  import { normalizeListOptions } from '@/helpers/utils';
23
+ import { useElementPosition } from '@/composition/usePosition';
23
24
 
24
25
  const emit = defineEmits<{
25
26
  (e: 'update:modelValue', v: M[]): void;
@@ -83,6 +84,7 @@ const data = reactive({
83
84
  search: '',
84
85
  activeOption: -1,
85
86
  open: false,
87
+ optionsHeight: 0,
86
88
  });
87
89
 
88
90
  const selectedValuesRef = computed(() => (Array.isArray(props.modelValue) ? props.modelValue : []));
@@ -149,7 +151,9 @@ const setFocusOnInput = () => input.value?.focus();
149
151
  const toggleModel = () => (data.open = !data.open);
150
152
 
151
153
  const onFocusOut = (event: FocusEvent) => {
152
- if (!rootRef?.value?.contains(event.relatedTarget as Node | null)) {
154
+ const relatedTarget = event.relatedTarget as Node | null;
155
+
156
+ if (!rootRef.value?.contains(relatedTarget) && !list.value?.contains(relatedTarget)) {
153
157
  data.search = '';
154
158
  data.open = false;
155
159
  }
@@ -220,20 +224,49 @@ watchPostEffect(() => {
220
224
  scrollIntoActive();
221
225
  }
222
226
  });
227
+
228
+ const optionsStyle = reactive({
229
+ top: '0px',
230
+ left: '0px',
231
+ width: '0px',
232
+ });
233
+
234
+ watch(list, (el) => {
235
+ if (el) {
236
+ const rect = el.getBoundingClientRect();
237
+ data.optionsHeight = rect.height;
238
+ window.dispatchEvent(new CustomEvent('adjust'));
239
+ }
240
+ });
241
+
242
+ useElementPosition(rootRef, (pos) => {
243
+ const focusWidth = 5; // see css
244
+
245
+ const downTopOffset = pos.top + pos.height + focusWidth;
246
+
247
+ if (downTopOffset + data.optionsHeight > pos.clientHeight) {
248
+ optionsStyle.top = pos.top - data.optionsHeight - focusWidth + 'px';
249
+ } else {
250
+ optionsStyle.top = downTopOffset + 'px';
251
+ }
252
+
253
+ optionsStyle.left = pos.left + 'px';
254
+ optionsStyle.width = pos.width + 'px';
255
+ });
223
256
  </script>
224
257
 
225
258
  <template>
226
- <div class="ui-multi-dropdown__envelope">
259
+ <div class="pl-multi-dropdown__envelope">
227
260
  <div
228
261
  ref="rootRef"
229
262
  :tabindex="tabindex"
230
- class="ui-multi-dropdown"
263
+ class="pl-multi-dropdown"
231
264
  :class="{ open: data.open, error, disabled }"
232
265
  @keydown="handleKeydown"
233
266
  @focusout="onFocusOut"
234
267
  >
235
- <div class="ui-multi-dropdown__container">
236
- <div class="ui-multi-dropdown__field">
268
+ <div class="pl-multi-dropdown__container">
269
+ <div class="pl-multi-dropdown__field">
237
270
  <input
238
271
  ref="input"
239
272
  v-model="data.search"
@@ -251,7 +284,7 @@ watchPostEffect(() => {
251
284
  </PlChip>
252
285
  </div>
253
286
  <div class="arrow" @click.stop="toggleModel" />
254
- <div class="ui-multi-dropdown__append">
287
+ <div class="pl-multi-dropdown__append">
255
288
  <slot name="append" />
256
289
  </div>
257
290
  </div>
@@ -264,29 +297,31 @@ watchPostEffect(() => {
264
297
  </template>
265
298
  </PlTooltip>
266
299
  </label>
267
- <div v-if="data.open" ref="list" class="ui-multi-dropdown__options">
268
- <div class="ui-multi-dropdown__open-chips-container">
269
- <PlChip v-for="(opt, i) in selectedOptionsRef" :key="i" closeable small @close="unselectOption(opt.value)">
270
- {{ opt.label || opt.value }}
271
- </PlChip>
300
+ <Teleport v-if="data.open" to="body">
301
+ <div ref="list" class="pl-multi-dropdown__options" :style="optionsStyle" tabindex="-1" @focusout="onFocusOut">
302
+ <div class="pl-multi-dropdown__open-chips-container">
303
+ <PlChip v-for="(opt, i) in selectedOptionsRef" :key="i" closeable small @close="unselectOption(opt.value)">
304
+ {{ opt.label || opt.value }}
305
+ </PlChip>
306
+ </div>
307
+ <DropdownListItem
308
+ v-for="(item, index) in filteredOptionsRef"
309
+ :key="index"
310
+ :option="item"
311
+ :text-item="'text'"
312
+ :is-selected="item.selected"
313
+ :is-hovered="data.activeOption == index"
314
+ size="medium"
315
+ use-checkbox
316
+ @click.stop="selectOption(item.value)"
317
+ />
318
+ <div v-if="!filteredOptionsRef.length" class="nothing-found">Nothing found</div>
272
319
  </div>
273
- <DropdownListItem
274
- v-for="(item, index) in filteredOptionsRef"
275
- :key="index"
276
- :option="item"
277
- :text-item="'text'"
278
- :is-selected="item.selected"
279
- :is-hovered="data.activeOption == index"
280
- size="medium"
281
- use-checkbox
282
- @click.stop="selectOption(item.value)"
283
- />
284
- <div v-if="!filteredOptionsRef.length" class="nothing-found">Nothing found</div>
285
- </div>
286
- <DoubleContour class="ui-multi-dropdown__contour" />
320
+ </Teleport>
321
+ <DoubleContour class="pl-multi-dropdown__contour" />
287
322
  </div>
288
323
  </div>
289
- <div v-if="error" class="ui-multi-dropdown__error">{{ error }}</div>
290
- <div v-else-if="helper" class="ui-multi-dropdown__helper">{{ helper }}</div>
324
+ <div v-if="error" class="pl-multi-dropdown__error">{{ error }}</div>
325
+ <div v-else-if="helper" class="pl-multi-dropdown__helper">{{ helper }}</div>
291
326
  </div>
292
327
  </template>
@@ -2,6 +2,7 @@ import { describe, it, expect } from 'vitest';
2
2
 
3
3
  import { mount } from '@vue/test-utils';
4
4
  import PlDropdown from '../PlDropdownMulti.vue';
5
+ import { delay } from '@milaboratories/helpers';
5
6
 
6
7
  describe('PlDropdownMulti', () => {
7
8
  it('modelValue', async () => {
@@ -18,16 +19,20 @@ describe('PlDropdownMulti', () => {
18
19
 
19
20
  await wrapper.find('input').trigger('focus');
20
21
 
21
- expect(await wrapper.findAll('.dropdown-list-item').length).toBe(2);
22
+ const getOptions = () => [...document.body.querySelectorAll('.dropdown-list-item')] as HTMLElement[];
22
23
 
23
- await wrapper
24
- .findAll('.dropdown-list-item')
25
- .filter((node) => node.text().match(/Option 2/))
26
- .at(0)
27
- ?.trigger('click');
24
+ const options = getOptions();
25
+
26
+ console.log('options', options);
27
+
28
+ expect(options.length).toBe(2);
29
+
30
+ options[1].click();
31
+
32
+ await delay(20);
28
33
 
29
34
  expect(wrapper.props('modelValue')).toEqual([1, 2]);
30
35
 
31
- expect(await wrapper.findAll('.dropdown-list-item').length).toBe(2); // options are not closed after click
36
+ expect(getOptions().length).toBe(2); // options are not closed after click
32
37
  });
33
38
  });
@@ -1,6 +1,6 @@
1
1
  @import "@/assets/mixins";
2
2
 
3
- .ui-multi-dropdown {
3
+ .pl-multi-dropdown {
4
4
  $root: &;
5
5
 
6
6
  --contour-color: var(--txt-01);
@@ -53,11 +53,14 @@
53
53
  }
54
54
 
55
55
  &__options {
56
- position: relative;
57
- background-color: var(--options-bg);
58
- border-radius: 0 0 6px 6px;
56
+ position: absolute;
57
+ top: 0;
58
+ z-index: var(--z-dropdown-options);
59
+ border: 1px solid var(--border-color-div-grey);
60
+ background-color: var(--pl-dropdown-options-bg);
61
+ border-radius: 6px;
59
62
  max-height: 244px;
60
- border-top: 1px solid var(--color-div-black);
63
+ box-shadow: 0px 4px 12px -2px rgba(15, 36, 77, 0.08), 0px 6px 24px -2px rgba(15, 36, 77, 0.08);
61
64
  @include scrollbar;
62
65
 
63
66
  .nothing-found {
@@ -83,7 +86,7 @@
83
86
  --base-icon: url('@/assets/images/24_checkbox-base.svg');
84
87
  --checked-icon: url('@/assets/images/24_checkbox-checked.svg');
85
88
 
86
- .ui-multi-dropdown__checkmark {
89
+ .pl-multi-dropdown__checkmark {
87
90
  cursor: pointer;
88
91
  outline: none;
89
92
  border-radius: 4px;
@@ -106,7 +109,7 @@
106
109
  &.selected {
107
110
  background-color: var(--color-active-select);
108
111
 
109
- .ui-multi-dropdown__checkmark {
112
+ .pl-multi-dropdown__checkmark {
110
113
  @include icon(var(--checked-icon), 24px);
111
114
  }
112
115
  }
@@ -290,7 +293,7 @@
290
293
  &__open-chips-container {
291
294
  padding: 12px;
292
295
 
293
- .ui-chip {
296
+ .pl-chip {
294
297
  margin-right: 4px;
295
298
  margin-bottom: 4px;
296
299
  }
@@ -69,6 +69,7 @@ const props = withDefaults(
69
69
  {
70
70
  label: '',
71
71
  helper: undefined,
72
+ loadingOptionsHelper: undefined,
72
73
  error: undefined,
73
74
  placeholder: '...',
74
75
  clearable: false,
@@ -2,6 +2,7 @@ import { describe, it, expect } from 'vitest';
2
2
 
3
3
  import { mount } from '@vue/test-utils';
4
4
  import PlDropdownRef from '../PlDropdownRef.vue';
5
+ import { delay } from '@milaboratories/helpers';
5
6
 
6
7
  describe('PlDropdownRef', () => {
7
8
  it('modelValue', async () => {
@@ -9,8 +10,8 @@ describe('PlDropdownRef', () => {
9
10
  props: {
10
11
  modelValue: {
11
12
  __isRef: true as const,
12
- blockId: '2',
13
- name: 'Ref to block 2',
13
+ blockId: '1',
14
+ name: 'Ref to block 1',
14
15
  },
15
16
  'onUpdate:modelValue': (e) => wrapper.setProps({ modelValue: e }),
16
17
  options: [
@@ -36,13 +37,15 @@ describe('PlDropdownRef', () => {
36
37
 
37
38
  await wrapper.find('input').trigger('focus');
38
39
 
39
- expect(await wrapper.findAll('.dropdown-list-item').length).toBe(2);
40
+ const options = [...document.body.querySelectorAll('.dropdown-list-item')] as HTMLElement[];
40
41
 
41
- await wrapper
42
- .findAll('.dropdown-list-item')
43
- .filter((node) => node.text().match(/Ref 2/))
44
- .at(0)
45
- ?.trigger('click');
42
+ expect(options.length).toBe(2);
43
+
44
+ expect(options.length).toBe(2);
45
+
46
+ options[1].click();
47
+
48
+ await delay(20);
46
49
 
47
50
  expect(wrapper.props('modelValue')).toStrictEqual({
48
51
  __isRef: true as const,
@@ -167,7 +167,7 @@ if (!props.cellStyle) {
167
167
  </PlTooltip>
168
168
  </label>
169
169
  <PlMaskIcon24 v-if="hasErrors" name="restart" />
170
- <PlMaskIcon24 v-else-if="isUploading" name="cloud-up" />
170
+ <PlMaskIcon24 v-else-if="isUploading" name="cloud-upload" />
171
171
  <PlMaskIcon24 v-else-if="isUploaded" name="success" />
172
172
  <PlMaskIcon24 v-else name="paper-clip" />
173
173
  <div :data-placeholder="placeholder ?? 'Choose file'" class="pl-file-input__filename" @click.stop="openFileDialog">
@@ -123,7 +123,7 @@ const fieldType = computed(() => {
123
123
  }
124
124
  });
125
125
 
126
- const passwordIcon = computed(() => (showPassword.value ? 'view-on' : 'view-off'));
126
+ const passwordIcon = computed(() => (showPassword.value ? 'view-show' : 'view-hide'));
127
127
 
128
128
  const clear = () => {
129
129
  if (props.clearable) {
@@ -7,8 +7,8 @@ export function useEventListener<T extends EventTarget, E extends EventMap[K], K
7
7
  target: MaybeRef<T | undefined>,
8
8
  type: K,
9
9
  callback: (this: T, evt: E) => void,
10
- capture = false,
10
+ options?: AddEventListenerOptions | boolean,
11
11
  ) {
12
- onMounted(() => unref(target)?.addEventListener(type, callback as (this: T, evt: Event) => void, capture));
13
- onUnmounted(() => unref(target)?.removeEventListener(type, callback as (this: T, evt: Event) => void, capture));
12
+ onMounted(() => unref(target)?.addEventListener(type, callback as (this: T, evt: Event) => void, options));
13
+ onUnmounted(() => unref(target)?.removeEventListener(type, callback as (this: T, evt: Event) => void, options));
14
14
  }
@@ -33,9 +33,9 @@ export function useElementPosition(el: Ref<HTMLElement | undefined>, cb: (pos: E
33
33
 
34
34
  onMounted(handle);
35
35
 
36
- useEventListener(window, 'scroll', handle, true);
36
+ useEventListener(window, 'scroll', handle, { capture: true, passive: true });
37
37
 
38
- useEventListener(window, 'resize', handle, true);
38
+ useEventListener(window, 'resize', handle, { passive: true });
39
39
 
40
40
  useEventListener(window, 'adjust', handle, true);
41
41
  }
@@ -6,6 +6,11 @@ export const maskIcons16 = [
6
6
  'arrow-right',
7
7
  'arrow-up',
8
8
  'box',
9
+ 'calendar',
10
+ 'caret-down',
11
+ 'caret-left',
12
+ 'caret-right',
13
+ 'caret-up',
9
14
  'cell-type-num',
10
15
  'cell-type-txt',
11
16
  'checkmark',
@@ -15,53 +20,46 @@ export const maskIcons16 = [
15
20
  'chevron-left',
16
21
  'chevron-right',
17
22
  'chevron-up',
18
- 'clear',
19
23
  'clipboard-copied',
20
24
  'clipboard',
21
25
  'close',
22
- 'comp',
23
- 'compare',
24
26
  'copy',
25
27
  'data-dimentions',
26
28
  'delete-bin',
27
29
  'delete-circle',
28
30
  'delete-clear',
29
- 'delete',
31
+ 'download',
30
32
  'drag-dots',
31
33
  'duplicate',
32
34
  'edit',
33
35
  'error',
34
36
  'export',
35
- 'filter-2',
36
37
  'filter-on',
37
38
  'filter',
38
39
  'help-outline',
39
40
  'help',
40
- 'import',
41
41
  'info-outline',
42
42
  'info',
43
43
  'jump-link',
44
- 'link-arrow',
45
44
  'link',
46
45
  'loading',
47
46
  'lock',
48
47
  'maximize',
49
48
  'minimize',
50
49
  'minus',
51
- 'more-horizontal',
52
50
  'more',
53
51
  'open',
52
+ 'paper-clip',
54
53
  'pause',
55
54
  'play',
56
- 'required',
55
+ 'renew',
57
56
  'restart',
58
57
  'reverse',
59
- 'settings-2',
60
58
  'settings',
61
59
  'sort',
62
- 'sorter',
63
60
  'stop',
64
61
  'success',
62
+ 'time',
65
63
  'tune',
66
64
  'warning',
67
65
  'x-axis',