@eturnity/eturnity_reusable_components 7.18.0-qa-dev03.0 → 7.20.0--EPDM-10564.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (56) hide show
  1. package/.storybook/preview.js +1 -1
  2. package/babel.config.js +2 -4
  3. package/package.json +18 -23
  4. package/src/App.vue +2 -2
  5. package/src/assets/svgIcons/split.svg +88 -6
  6. package/src/components/addNewButton/index.vue +5 -3
  7. package/src/components/buttons/buttonIcon/index.vue +1 -1
  8. package/src/components/buttons/closeButton/index.vue +1 -1
  9. package/src/components/buttons/mainButton/index.vue +6 -1
  10. package/src/components/card/index.vue +25 -24
  11. package/src/components/deleteIcon/DeleteIcon.stories.js +7 -7
  12. package/src/components/deleteIcon/index.vue +47 -30
  13. package/src/components/draggableInputHandle/index.vue +24 -25
  14. package/src/components/dropdown/index.vue +129 -110
  15. package/src/components/errorMessage/index.vue +10 -5
  16. package/src/components/filter/filterSettings.vue +59 -97
  17. package/src/components/filter/index.vue +3 -3
  18. package/src/components/filter/parentDropdown.vue +2 -2
  19. package/src/components/icon/iconCache.js +23 -0
  20. package/src/components/icon/iconCollection.vue +2 -2
  21. package/src/components/icon/index.vue +67 -75
  22. package/src/components/iconWrapper/index.vue +1 -4
  23. package/src/components/infoCard/index.vue +2 -3
  24. package/src/components/infoText/index.vue +2 -2
  25. package/src/components/inputs/checkbox/index.vue +21 -6
  26. package/src/components/inputs/inputNumber/index.vue +10 -10
  27. package/src/components/inputs/inputNumberQuestion/index.vue +1 -1
  28. package/src/components/inputs/inputText/index.vue +3 -3
  29. package/src/components/inputs/radioButton/index.vue +1 -1
  30. package/src/components/inputs/searchInput/index.vue +28 -11
  31. package/src/components/inputs/select/index.vue +295 -67
  32. package/src/components/inputs/select/option/index.vue +49 -12
  33. package/src/components/inputs/slider/index.vue +16 -16
  34. package/src/components/inputs/switchField/index.vue +2 -2
  35. package/src/components/inputs/textAreaInput/index.vue +1 -1
  36. package/src/components/inputs/toggle/index.vue +2 -2
  37. package/src/components/label/index.vue +27 -31
  38. package/src/components/markerItem/index.vue +86 -0
  39. package/src/components/modals/modal/index.vue +2 -6
  40. package/src/components/navigationTabs/index.vue +27 -20
  41. package/src/components/pageSubtitle/index.vue +1 -1
  42. package/src/components/pageTitle/index.vue +13 -18
  43. package/src/components/pagination/index.vue +3 -3
  44. package/src/components/progressBar/index.vue +1 -1
  45. package/src/components/projectMarker/index.vue +16 -9
  46. package/src/components/sideMenu/index.vue +1 -1
  47. package/src/components/spinner/index.vue +7 -11
  48. package/src/components/tableDropdown/index.vue +35 -45
  49. package/src/components/tables/mainTable/exampleNested.vue +1 -1
  50. package/src/components/tables/mainTable/index.vue +10 -9
  51. package/src/components/tables/viewTable/index.vue +2 -2
  52. package/src/components/threeDots/index.vue +1 -1
  53. package/src/components/videoThumbnail/index.vue +95 -100
  54. package/src/main.js +4 -11
  55. package/src/assets/svgIcons/anchor.svg +0 -18
  56. package/src/assets/svgIcons/flatten_roof.svg +0 -20
@@ -1,12 +1,14 @@
1
1
  <template>
2
2
  <Container
3
3
  :selectWidth="selectWidth"
4
+ :noRelative="noRelative"
4
5
  @mouseenter="mouseEnterHandler"
5
6
  @mouseleave="mouseLeaveHandler"
6
7
  >
7
8
  <input-wrapper
8
9
  :hasLabel="!!label && label.length > 0"
9
10
  :alignItems="alignItems"
11
+ :noRelative="noRelative"
10
12
  >
11
13
  <label-wrapper v-if="label" :data-id="labelDataId">
12
14
  <input-label
@@ -28,7 +30,9 @@
28
30
  <select-button-wrapper :disabled="disabled">
29
31
  <selectButton
30
32
  ref="select"
33
+ class="select-button"
31
34
  @click="toggleDropdown"
35
+ :selectWidth="selectWidth"
32
36
  :selectHeight="selectHeight"
33
37
  :height="height"
34
38
  :selectMinHeight="selectMinHeight"
@@ -39,12 +43,15 @@
39
43
  buttonFontColor || colorMode == 'dark' ? 'white' : 'black'
40
44
  "
41
45
  :hasError="hasError"
42
- :isSearchBarVisible="isSearchBarVisible"
46
+ :hasNoPadding="isSearchBarVisible || !hasSelectButtonPadding"
43
47
  :disabled="disabled"
44
- @keydown.native="onKeyDown"
48
+ @keydown="onKeyDown"
45
49
  :showBorder="showBorder"
46
50
  :data-id="dataId"
47
51
  :paddingLeft="paddingLeft"
52
+ :tablePaddingLeft="tablePaddingLeft"
53
+ :noRelative="noRelative"
54
+ :showDisabledBackground="showDisabledBackground"
48
55
  >
49
56
  <draggableInputHandle
50
57
  v-if="isDraggable && !isSearchBarVisible"
@@ -64,7 +71,7 @@
64
71
  :value="textSearch"
65
72
  @keydown.stop="onKeyDown"
66
73
  @input-change="searchChange"
67
- @click.native.stop
74
+ @click.stop
68
75
  />
69
76
  <selector
70
77
  v-else
@@ -74,7 +81,7 @@
74
81
  >
75
82
  <slot name="selector" :selectedValue="selectedValue"></slot>
76
83
  </selector>
77
- <Caret @click.stop="toggleCaretDropdown">
84
+ <Caret @click.stop="toggleCaretDropdown" class="caret_dropdown">
78
85
  <icon
79
86
  v-if="isDropdownOpen"
80
87
  name="arrow_up"
@@ -97,26 +104,36 @@
97
104
  />
98
105
  </Caret>
99
106
  </selectButton>
100
- <DropdownWrapper>
101
- <selectDropdown
102
- ref="dropdown"
103
- v-show="isDropdownOpen"
104
- :hoveredIndex="hoveredIndex"
105
- :hoveredValue="hoveredValue"
106
- :isActive="isActive"
107
- :optionWidth="optionWidth"
108
- :hoveredBgColor="colorMode == 'dark' ? '#000000' : dropdownBgColor"
109
- :bgColor="colorMode == 'dark' ? 'black' : dropdownBgColor"
110
- :fontColor="
111
- dropdownFontColor || colorMode == 'dark' ? 'white' : 'black'
112
- "
113
- :selectedValue="selectedValue"
114
- @option-selected="optionSelected"
115
- @option-hovered="optionHovered"
116
- @mouseleave="optionLeave"
117
- >
118
- <slot name="dropdown"></slot>
119
- </selectDropdown>
107
+ <DropdownWrapper ref="dropdownWrapperRef" :noRelative="noRelative">
108
+ <Teleport to="#portal-target">
109
+ <selectDropdown
110
+ ref="dropdown"
111
+ v-show="isSelectDropdownShown"
112
+ :dropdownPosition="dropdownPosition"
113
+ :hoveredIndex="hoveredIndex"
114
+ :hoveredValue="hoveredValue"
115
+ :isActive="isActive"
116
+ :optionWidth="getOptionWidth"
117
+ :hoveredBgColor="
118
+ colorMode == 'dark' ? '#000000' : dropdownBgColor
119
+ "
120
+ :bgColor="
121
+ dropdownBgColor || colorMode == 'dark' ? 'black' : 'white'
122
+ "
123
+ :fontColor="
124
+ dropdownFontColor || colorMode == 'dark' ? 'white' : 'black'
125
+ "
126
+ :noRelative="noRelative"
127
+ :fontSize="fontSize"
128
+ :minWidth="minWidth"
129
+ :selectedValue="selectedValue"
130
+ @option-selected="optionSelected"
131
+ @option-hovered="optionHovered"
132
+ @mouseleave="optionLeave"
133
+ >
134
+ <slot name="dropdown"></slot>
135
+ </selectDropdown>
136
+ </Teleport>
120
137
  </DropdownWrapper>
121
138
  </select-button-wrapper>
122
139
  </input-wrapper>
@@ -131,8 +148,9 @@
131
148
  // optionWidth="50%"
132
149
  // label="that is a label"
133
150
  // alignItems="vertical"
134
- // label-data-id="test-label0data-id"
151
+ // label-data-id="test-label-data-id"
135
152
  // data-id="test-data-id"
153
+ // :hasSelectButtonPadding="false"
136
154
  // >
137
155
  // <template #selector="{selectedValue}">
138
156
  // value selected: {{selectedValue}}
@@ -145,7 +163,8 @@
145
163
  // </template>
146
164
  // </Select>
147
165
 
148
- import styled from 'vue-styled-components'
166
+ import { Teleport } from 'vue'
167
+ import styled from 'vue3-styled-components'
149
168
  import InfoText from '../../infoText'
150
169
  import icon from '../../icon'
151
170
  import inputText from '../inputText'
@@ -161,7 +180,7 @@ const Caret = styled.div`
161
180
  width: ${CARET_WIDTH};
162
181
  min-width: ${CARET_WIDTH};
163
182
  height: 100%;
164
- align-items: stretch;
183
+ align-items: center;
165
184
  cursor: pointer;
166
185
  margin-left: auto;
167
186
  `
@@ -172,7 +191,10 @@ const selectorProps = {
172
191
  showBorder: Boolean
173
192
  }
174
193
  const Selector = styled('div', selectorProps)`
175
- ${(props) => props.selectWidth === '100%' ? 'width: 100%;' : `width: calc(${props.selectWidth} -
194
+ ${(props) =>
195
+ props.selectWidth === '100%'
196
+ ? 'width: 100%;'
197
+ : `width: calc(${props.selectWidth} -
176
198
  (
177
199
  ${CARET_WIDTH} +
178
200
  ${props.paddingLeft}
@@ -181,8 +203,7 @@ const Selector = styled('div', selectorProps)`
181
203
  );
182
204
  white-space: nowrap;
183
205
  text-overflow: ellipsis;
184
- overflow: hidden;`
185
- }
206
+ overflow: hidden;`}
186
207
  `
187
208
 
188
209
  const labelAttrs = { fontSize: String, fontColor: String }
@@ -197,10 +218,14 @@ const InputLabel = styled('div', labelAttrs)`
197
218
  const optionalLabel = styled.span`
198
219
  font-weight: 300;
199
220
  `
200
- const inputProps = { selectWidth: String, optionWidth: String }
221
+ const inputProps = {
222
+ selectWidth: String,
223
+ optionWidth: String,
224
+ noRelative: Boolean
225
+ }
201
226
  const Container = styled('div', inputProps)`
202
227
  width: ${(props) => props.selectWidth};
203
- position: relative;
228
+ position: ${(props) => (props.noRelative ? 'static' : 'relative')};
204
229
  display: inline-block;
205
230
  `
206
231
  const LabelWrapper = styled.div`
@@ -224,18 +249,29 @@ const selectButtonAttrs = {
224
249
  hasError: Boolean,
225
250
  disabled: Boolean,
226
251
  selectHeight: String,
252
+ selectWidth: String,
227
253
  height: String,
228
254
  selectMinHeight: String,
229
- isSearchBarVisible: Boolean,
255
+ hasNoPadding: Boolean,
230
256
  showBorder: Boolean,
231
- paddingLeft: String
257
+ paddingLeft: String,
258
+ noRelative: Boolean,
259
+ tablePaddingLeft: String,
260
+ showDisabledBackground: Boolean
232
261
  }
233
262
  const selectButton = styled('div', selectButtonAttrs)`
234
- position: relative;
263
+ position: ${(props) => (props.noRelative ? 'static' : 'relative')};
235
264
  box-sizing: border-box;
236
265
  border-radius: 4px;
266
+ max-width: ${(props) => (props.selectWidth ? props.selectWidth : '100%')};
237
267
  ${(props) =>
238
- props.isSearchBarVisible ? '' : `padding-left: ${props.paddingLeft}`};
268
+ props.isSearchBarVisible
269
+ ? ''
270
+ : `padding-left: ${
271
+ props.tablePaddingLeft ? props.tablePaddingLeft : props.paddingLeft
272
+ }`};
273
+ ${(props) =>
274
+ props.hasNoPadding ? '' : `padding-left: ${props.paddingLeft}`};
239
275
  text-align: left;
240
276
  min-height: ${(props) =>
241
277
  props.selectHeight
@@ -247,7 +283,7 @@ const selectButton = styled('div', selectButtonAttrs)`
247
283
  : '36px'};
248
284
  display: flex;
249
285
  align-items: center;
250
- max-height: ${(props) => props.selectHeight};
286
+ height: ${(props) => props.selectHeight};
251
287
  ${({ showBorder, theme, hasError }) =>
252
288
  showBorder &&
253
289
  `
@@ -256,7 +292,7 @@ const selectButton = styled('div', selectButtonAttrs)`
256
292
  }
257
293
  `}
258
294
  background-color:${(props) =>
259
- props.disabled
295
+ props.disabled && props.showDisabledBackground
260
296
  ? props.theme.colors.grey5
261
297
  : props.theme.colors[props.bgColor]
262
298
  ? props.theme.colors[props.bgColor]
@@ -279,14 +315,20 @@ const selectDropdownAttrs = {
279
315
  fontColor: String,
280
316
  optionWidth: String,
281
317
  hoveredIndex: Number,
318
+ fontSize: String,
319
+ dropdownPosition: Object,
282
320
  hoveredValue: Number | String,
283
- selectedValue: Number | String
321
+ selectedValue: Number | String,
322
+ noRelative: Boolean,
323
+ minWidth: String
284
324
  }
285
325
  const selectDropdown = styled('div', selectDropdownAttrs)`
286
326
  box-sizing: border-box;
287
- z-index: ${(props) => (props.isActive ? '2' : '1')};
327
+ z-index: ${(props) => (props.isActive ? '2' : '99999')};
288
328
  position: absolute;
289
- top: 5px;
329
+ top: ${(props) =>
330
+ props.noRelative ? 'auto' : props.dropdownPosition?.top + 'px'};
331
+ left: ${(props) => props.dropdownPosition?.left}px;
290
332
  border: ${BORDER_WIDTH} solid ${(props) => props.theme.colors.grey4};
291
333
  border-radius: 4px;
292
334
  display: flex;
@@ -295,6 +337,7 @@ const selectDropdown = styled('div', selectDropdownAttrs)`
295
337
  padding: 0px;
296
338
  box-shadow: 0px 8px 16px 0px rgba(0, 0, 0, 0.2);
297
339
  width: ${(props) => (props.optionWidth ? props.optionWidth : '100%')};
340
+ min-width: ${(props) => (props.minWidth ? props.minWidth : '100%')};
298
341
  background-color: ${(props) =>
299
342
  props.theme.colors[props.bgColor]
300
343
  ? props.theme.colors[props.bgColor]
@@ -311,19 +354,36 @@ const selectDropdown = styled('div', selectDropdownAttrs)`
311
354
  ? props.theme.colors[props.hoveredBgColor]
312
355
  : props.hoveredBgColor};
313
356
  }
357
+ font-size: ${(props) => props.fontSize};
314
358
  `
315
- const DropdownWrapper = styled('div')`
316
- position: relative;
359
+ selectDropdown.emits = ['option-hovered', 'option-selected']
360
+ const DropdownAttrs = { noRelative: Boolean }
361
+ const DropdownWrapper = styled('div', DropdownAttrs)`
362
+ position: ${(props) => (props.noRelative ? 'static' : 'relative')};
317
363
  `
318
- const inputAttrs = { alignItems: String, hasLabel: Boolean }
364
+ const inputAttrs = {
365
+ alignItems: String,
366
+ hasLabel: Boolean,
367
+ noRelative: Boolean
368
+ }
319
369
  const InputWrapper = styled('div', inputAttrs)`
320
- position: relative;
370
+ position: ${(props) => (props.noRelative ? 'static' : 'relative')};
321
371
  display: grid;
322
372
  align-items: center;
323
373
  gap: 8px;
324
374
  grid-template-columns: ${(props) =>
325
375
  props.alignItems === 'vertical' || !props.hasLabel ? '1fr' : 'auto 1fr'};
326
376
  `
377
+
378
+ const DROPDOWN_HEIGHT_OFFSET = 4
379
+ const DROPDOWN_TOP_OFFSET = 21
380
+ const MIN_OPTION_LENGTH = 5
381
+
382
+ const DROPDOWN_MENU_POSITIONS = {
383
+ Automatic: 'automatic',
384
+ Bottom: 'bottom'
385
+ }
386
+
327
387
  export default {
328
388
  name: 'RCselect',
329
389
 
@@ -336,6 +396,10 @@ export default {
336
396
  required: false,
337
397
  default: '13px'
338
398
  },
399
+ noRelative: {
400
+ required: false,
401
+ default: false
402
+ },
339
403
  label: {
340
404
  required: false
341
405
  },
@@ -368,6 +432,9 @@ export default {
368
432
  required: false,
369
433
  default: '36px'
370
434
  },
435
+ minWidth: {
436
+ required: false
437
+ },
371
438
  optionWidth: {
372
439
  required: false,
373
440
  default: null
@@ -435,9 +502,28 @@ export default {
435
502
  type: String,
436
503
  default: ''
437
504
  },
505
+ hasSelectButtonPadding: {
506
+ type: Boolean,
507
+ default: true
508
+ },
438
509
  isDraggable: {
439
510
  type: Boolean,
440
511
  default: false
512
+ },
513
+ tablePaddingLeft: {
514
+ required: false
515
+ },
516
+ showDisabledBackground: {
517
+ required: false,
518
+ default: true
519
+ },
520
+ minOptionLength: {
521
+ type: Number,
522
+ default: MIN_OPTION_LENGTH
523
+ },
524
+ dropdownMenuPosition: {
525
+ type: String,
526
+ default: DROPDOWN_MENU_POSITIONS.Automatic // options: ['automatic', bottom]
441
527
  }
442
528
  },
443
529
 
@@ -456,6 +542,7 @@ export default {
456
542
  Caret,
457
543
  Selector,
458
544
  inputText,
545
+ Teleport,
459
546
  draggableInputHandle
460
547
  },
461
548
 
@@ -467,23 +554,38 @@ export default {
467
554
  isActive: false,
468
555
  textSearch: '',
469
556
  hoveredIndex: 0,
470
- hoveredValue: null,
471
- isClickOutsideActive: false
557
+ isClickOutsideActive: false,
558
+ dropdownPosition: {
559
+ left: null,
560
+ top: null
561
+ },
562
+ dropdownWidth: null,
563
+ hoveredValue: null
472
564
  }
473
565
  },
474
566
  mounted() {
567
+ this.observeDropdownHeight()
568
+ this.observeSelectWidth()
569
+ window.addEventListener('resize', this.handleSetDropdownOffet)
570
+ },
571
+ beforeMount() {
475
572
  this.selectedValue = this.value
476
573
  document.addEventListener('click', this.clickOutside)
574
+ this.getDropdownPosition()
575
+ window.removeEventListener('resize', this.handleSetDropdownOffet)
576
+ if (this.dropdownResizeObserver) this.dropdownResizeObserver.disconnect()
577
+ if (this.selectResizeObserver) this.selectResizeObserver.disconnect()
477
578
  },
478
- beforeDestroy() {
579
+ unmounted() {
479
580
  document.removeEventListener('click', this.clickOutside)
480
581
  },
481
582
  methods: {
482
583
  focus() {
483
584
  this.isActive = true
484
585
  },
485
- blur() {
586
+ blur(e) {
486
587
  this.isActive = false
588
+ this.$emit('blur', e)
487
589
  },
488
590
  toggleDropdown() {
489
591
  this.isDropdownOpen = !this.isDropdownOpen
@@ -499,6 +601,9 @@ export default {
499
601
  this.blur()
500
602
  this.isDropdownOpen = false
501
603
  },
604
+ clearSearch() {
605
+ this.textSearch = ''
606
+ },
502
607
  optionSelected(e) {
503
608
  this.selectedValue = e
504
609
  this.closeDropdown()
@@ -527,15 +632,15 @@ export default {
527
632
  searchChange(value) {
528
633
  this.textSearch = value
529
634
  this.$emit('search-change', value)
530
- this.$refs.dropdown.$children
531
- .map((component) => component.$el)
532
- .forEach((el) => {
533
- if (!el.textContent.toLowerCase().includes(value.toLowerCase())) {
534
- el.style.display = 'none'
535
- } else {
536
- el.style.display = 'inherit'
537
- }
538
- })
635
+ const dropdownChildren = [...this.$refs.dropdown.$el.children]
636
+ dropdownChildren.forEach((el) => {
637
+ if (!el.textContent.toLowerCase().includes(value.toLowerCase())) {
638
+ el.style.display = 'none'
639
+
640
+ return
641
+ }
642
+ el.style.display = 'inherit'
643
+ })
539
644
  },
540
645
  clickOutside(event) {
541
646
  const dropdownRef = this.$refs.dropdown
@@ -545,6 +650,7 @@ export default {
545
650
  if (
546
651
  this.$refs.select.$el == event.target ||
547
652
  this.$refs.select.$el.contains(event.target) ||
653
+ event.target.id === 'more-button' ||
548
654
  event.target.parentNode === dropdownRef.$el
549
655
  ) {
550
656
  return
@@ -558,13 +664,101 @@ export default {
558
664
  } else if (e.key == 'ArrowUp') {
559
665
  this.onArrowPress(-1)
560
666
  } else if (e.key == 'Enter') {
561
- const optionHoveredComponent =
562
- this.$refs.dropdown.$children[
563
- (this.hoveredIndex - 1 + this.optionLength) % this.optionLength
564
- ]
667
+ const optionHoveredComponent = [...this.$refs.dropdown.$el.children][
668
+ (this.hoveredIndex - 1 + this.optionLength) % this.optionLength
669
+ ]
565
670
  this.optionSelected(optionHoveredComponent.$el.dataset.value)
566
671
  }
567
672
  },
673
+ // If some part of the dropdown menu is outside viewport of the bottom of the screen,
674
+ // we need to offset it and display it at the top of the select dropdown instead
675
+ async getDropdownPosition() {
676
+ if (
677
+ !this.$refs.dropdownWrapperRef ||
678
+ !this.$refs.select ||
679
+ !this.$refs.dropdown
680
+ ) {
681
+ return
682
+ }
683
+ await this.$nextTick()
684
+ const isDisplayedAtBottom = await this.generateDropdownPosition()
685
+ // If the dropdown menu is going to be displayed at the bottom,
686
+ // we need reverify its position after a dom update (nextTick)
687
+ await this.$nextTick()
688
+ if (isDisplayedAtBottom) this.generateDropdownPosition()
689
+ },
690
+ async generateDropdownPosition() {
691
+ const isDropdownNotCompletelyVisible =
692
+ await this.isBottomOfDropdownOutOfViewport()
693
+ const dropdownWrapperEl = this.$refs.dropdownWrapperRef.$el
694
+ const selectButtonHeight = this.$refs.select.$el.clientHeight
695
+ const dropdownHeight = this.$refs.dropdown.$el.clientHeight
696
+ const dropdownWrapperRelativeHeight =
697
+ dropdownWrapperEl.getBoundingClientRect().top +
698
+ window.scrollY +
699
+ DROPDOWN_HEIGHT_OFFSET
700
+
701
+ const top =
702
+ isDropdownNotCompletelyVisible ||
703
+ (!isDropdownNotCompletelyVisible &&
704
+ this.dropdownMenuPosition === DROPDOWN_MENU_POSITIONS.Bottom)
705
+ ? dropdownWrapperRelativeHeight
706
+ : dropdownWrapperRelativeHeight -
707
+ dropdownHeight -
708
+ selectButtonHeight -
709
+ DROPDOWN_TOP_OFFSET
710
+ const left = this.dropdownPosition.left
711
+ ? this.dropdownPosition.left
712
+ : dropdownWrapperEl.getBoundingClientRect().left + window.scrollX
713
+
714
+ this.dropdownPosition = { left: Math.floor(left), top: Math.floor(top) }
715
+
716
+ return isDropdownNotCompletelyVisible
717
+ },
718
+ async isBottomOfDropdownOutOfViewport() {
719
+ if (
720
+ !this.$refs.dropdown ||
721
+ this.dropdownMenuPosition === DROPDOWN_MENU_POSITIONS.Bottom
722
+ ) {
723
+ return false
724
+ }
725
+
726
+ await this.$nextTick()
727
+ const rect = this.$refs.dropdown.$el.getBoundingClientRect()
728
+ const windowHeight =
729
+ window.innerHeight || document.documentElement.clientHeight
730
+
731
+ if (windowHeight <= 650) return true
732
+
733
+ // using Math.floor because the offsets may contain decimals we are not going to consider here
734
+ return Math.floor(rect.top) + Math.floor(rect.height) <= windowHeight
735
+ },
736
+ observeDropdownHeight() {
737
+ if (!this.$refs.dropdown) return
738
+ this.dropdownResizeObserver = new ResizeObserver(() => {
739
+ this.$nextTick(() => this.getDropdownPosition())
740
+ })
741
+ this.dropdownResizeObserver.observe(this.$refs.dropdown.$el)
742
+ },
743
+ handleSetDropdownOffet() {
744
+ if (!this.$refs.select) return
745
+ this.dropdownPosition.left = Math.floor(
746
+ this.$refs.select.$el.getBoundingClientRect().left
747
+ )
748
+ this.getDropdownWidth()
749
+ },
750
+ observeSelectWidth() {
751
+ if (!this.$refs.select) return
752
+ this.selectResizeObserver = new ResizeObserver(() =>
753
+ this.$nextTick(() => this.getDropdownWidth())
754
+ )
755
+ this.selectResizeObserver.observe(this.$refs.dropdown.$el)
756
+ },
757
+ async getDropdownWidth() {
758
+ if (!this.$refs.select) return
759
+ await this.$nextTick()
760
+ this.dropdownWidth = `${this.$refs.select.$el.clientWidth}px`
761
+ },
568
762
  onArrowPress(dir) {
569
763
  let newHoveredElem
570
764
  const currentHoveredElem = this.$refs.dropdown.$el.querySelector(
@@ -590,33 +784,67 @@ export default {
590
784
  computed: {
591
785
  optionLength() {
592
786
  if (this.isDropdownOpen) {
593
- return this.$refs.dropdown.$el.childElementCount
787
+ return this.$refs.dropdown.$el.childElementCount > 1
788
+ ? this.$refs.dropdown.$el.childElementCount
789
+ : this.$refs.dropdown.$el.children[0].childElementCount
594
790
  }
595
791
 
596
792
  return 0
597
793
  },
598
794
  isSearchBarVisible() {
599
- return this.isSearchable && this.isDropdownOpen
795
+ return (
796
+ this.isSearchable &&
797
+ this.optionLength >= this.minOptionLength &&
798
+ this.isDropdownOpen
799
+ )
800
+ },
801
+ getOptionWidth() {
802
+ if (this.optionWidth) return this.optionWidth
803
+
804
+ return this.dropdownWidth
805
+ },
806
+ isSelectDropdownShown() {
807
+ return (
808
+ this.isDropdownOpen &&
809
+ this.dropdownPosition.left !== null &&
810
+ (!this.isSearchable || this.isSearchable)
811
+ )
812
+ },
813
+ isMobileDevice() {
814
+ const userAgent = navigator.userAgent || navigator.vendor || window.opera
815
+ const touchCapable =
816
+ 'ontouchstart' in window ||
817
+ navigator.maxTouchPoints > 0 ||
818
+ navigator.msMaxTouchPoints > 0
819
+
820
+ return (
821
+ /Android/i.test(userAgent) ||
822
+ /iPad|iPhone|iPod/.test(userAgent) ||
823
+ (/Macintosh/.test(userAgent) && touchCapable) ||
824
+ /windows phone/i.test(userAgent)
825
+ )
600
826
  }
601
827
  },
602
828
  watch: {
603
829
  value(val) {
604
830
  this.selectedValue = val
605
831
  },
606
- isDropdownOpen(val) {
607
- this.$emit('is-dropdown-open', val)
832
+ async isDropdownOpen(val) {
608
833
  if (val) {
609
834
  setTimeout(() => {
610
835
  this.isClickOutsideActive = true
611
836
  }, 10)
837
+ await this.$nextTick()
838
+ this.handleSetDropdownOffet()
612
839
  } else {
840
+ this.dropdownPosition.left = null
613
841
  setTimeout(() => {
614
842
  this.isClickOutsideActive = false
615
843
  }, 10)
616
844
  }
617
845
  if (val && this.isSearchable) {
618
846
  this.$nextTick(() => {
619
- if (this.$refs.searchInput) {
847
+ if (this.$refs.searchInput && !this.isMobileDevice) {
620
848
  this.$refs.searchInput.$el.querySelector('input').focus()
621
849
  }
622
850
  })