@eturnity/eturnity_reusable_components 7.18.0--EPDM-9013.1 → 7.18.0-EPDM-10335.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 (52) hide show
  1. package/.storybook/preview.js +1 -1
  2. package/package.json +19 -23
  3. package/src/App.vue +2 -2
  4. package/src/components/addNewButton/index.vue +5 -3
  5. package/src/components/buttons/buttonIcon/index.vue +1 -1
  6. package/src/components/buttons/closeButton/index.vue +1 -1
  7. package/src/components/buttons/mainButton/index.vue +6 -1
  8. package/src/components/deleteIcon/DeleteIcon.stories.js +7 -7
  9. package/src/components/deleteIcon/index.vue +25 -21
  10. package/src/components/draggableInputHandle/index.vue +24 -25
  11. package/src/components/dropdown/index.vue +129 -110
  12. package/src/components/errorMessage/index.vue +10 -5
  13. package/src/components/filter/filterSettings.vue +58 -97
  14. package/src/components/filter/index.vue +3 -3
  15. package/src/components/filter/parentDropdown.vue +2 -2
  16. package/src/components/icon/iconCache.js +23 -0
  17. package/src/components/icon/iconCollection.vue +2 -2
  18. package/src/components/icon/index.vue +67 -75
  19. package/src/components/iconWrapper/index.vue +1 -4
  20. package/src/components/infoCard/index.vue +2 -3
  21. package/src/components/infoText/index.vue +1 -1
  22. package/src/components/inputs/checkbox/index.vue +21 -6
  23. package/src/components/inputs/inputNumber/index.vue +8 -11
  24. package/src/components/inputs/inputNumberQuestion/index.vue +1 -1
  25. package/src/components/inputs/inputText/index.vue +3 -3
  26. package/src/components/inputs/radioButton/index.vue +1 -1
  27. package/src/components/inputs/searchInput/index.vue +28 -11
  28. package/src/components/inputs/select/index.vue +199 -55
  29. package/src/components/inputs/select/option/index.vue +36 -11
  30. package/src/components/inputs/slider/index.vue +16 -16
  31. package/src/components/inputs/switchField/index.vue +2 -2
  32. package/src/components/inputs/textAreaInput/index.vue +1 -1
  33. package/src/components/inputs/toggle/index.vue +2 -2
  34. package/src/components/label/index.vue +27 -31
  35. package/src/components/modals/modal/index.vue +2 -6
  36. package/src/components/navigationTabs/index.vue +27 -20
  37. package/src/components/pageSubtitle/index.vue +1 -1
  38. package/src/components/pageTitle/index.vue +4 -4
  39. package/src/components/pagination/index.vue +1 -1
  40. package/src/components/progressBar/index.vue +1 -1
  41. package/src/components/projectMarker/index.vue +16 -9
  42. package/src/components/sideMenu/index.vue +1 -1
  43. package/src/components/spinner/index.vue +7 -11
  44. package/src/components/tableDropdown/index.vue +30 -37
  45. package/src/components/tables/mainTable/exampleNested.vue +1 -1
  46. package/src/components/tables/mainTable/index.vue +10 -9
  47. package/src/components/tables/viewTable/index.vue +2 -2
  48. package/src/components/threeDots/index.vue +1 -1
  49. package/src/components/videoThumbnail/index.vue +95 -100
  50. package/src/main.js +4 -11
  51. package/src/assets/svgIcons/bexio.svg +0 -4
  52. package/src/assets/svgIcons/data_transfer.svg +0 -3
@@ -47,7 +47,6 @@
47
47
  :fontSize="fontSize"
48
48
  :fontColor="fontColor"
49
49
  :backgroundColor="backgroundColor"
50
- v-on="$listeners"
51
50
  :hasSlot="hasSlot"
52
51
  :hasLabelSlot="hasLabelSlot"
53
52
  :slotSize="slotSize"
@@ -101,7 +100,7 @@
101
100
  // >
102
101
  //<template name=label><img>....</template>
103
102
  //</inputNumber>
104
- import styled from 'vue-styled-components'
103
+ import styled from 'vue3-styled-components'
105
104
  import {
106
105
  stringToNumber,
107
106
  numberToString
@@ -469,7 +468,7 @@ export default {
469
468
  }
470
469
  },
471
470
  methods: {
472
- onEnterPress(){
471
+ onEnterPress() {
473
472
  this.$emit('on-enter-click')
474
473
  this.$refs.inputField1.$el.blur()
475
474
  },
@@ -488,9 +487,10 @@ export default {
488
487
  // Need to return an integer rather than a string
489
488
  this.$emit('input-change', event)
490
489
  },
491
- onEvaluateCode(val) {
490
+ onEvaluateCode(event) {
492
491
  // function to perform math on the code
493
492
  // filter the string in case of any malicious content
493
+ const val = event.target.value
494
494
  let filtered = val.replace('(auto)', '').replace(/[^-()\d/*+.,]/g, '')
495
495
  filtered = filtered.split(/([-+*/()])/)
496
496
  let formatted = filtered.map((item) => {
@@ -548,20 +548,17 @@ export default {
548
548
  }
549
549
  return array
550
550
  },
551
- onInput(value) {
552
- // if(!this.isFocused){
553
- // return
554
- // }
551
+ onInput(event) {
555
552
  if (this.isBlurred) {
556
553
  this.isBlurred = false
557
554
  return
558
555
  }
559
- if (value === '') {
556
+ if (event.target.value === '') {
560
557
  this.$emit('on-input', '')
561
558
  }
562
559
  let evaluatedVal
563
560
  try {
564
- evaluatedVal = this.onEvaluateCode(value)
561
+ evaluatedVal = this.onEvaluateCode(event)
565
562
  } finally {
566
563
  if (evaluatedVal) {
567
564
  this.$emit('on-input', evaluatedVal)
@@ -573,7 +570,7 @@ export default {
573
570
  // setting isBlurred so we don't trigger onInput as well
574
571
  this.isBlurred = true
575
572
  let value = e.target.value
576
- let evaluatedInput = this.onEvaluateCode(value)
573
+ let evaluatedInput = this.onEvaluateCode(e)
577
574
  this.onChangeHandler(evaluatedInput ? evaluatedInput : value)
578
575
  if ((evaluatedInput && value.length) || this.minNumber !== null) {
579
576
  this.textInput = numberToString({
@@ -44,7 +44,7 @@
44
44
  // number_max_allowed: 10,
45
45
  // unit_short_name: "cm",
46
46
  // },
47
- import styled from "vue-styled-components"
47
+ import styled from "vue3-styled-components"
48
48
  import {
49
49
  stringToNumber,
50
50
  numberToString,
@@ -66,7 +66,7 @@
66
66
  </template>
67
67
 
68
68
  <script>
69
- import styled from 'vue-styled-components'
69
+ import styled from 'vue3-styled-components'
70
70
  import InfoText from '../../infoText'
71
71
  import Icon from '../../icon'
72
72
  import ErrorMessage from '../../errorMessage'
@@ -332,8 +332,8 @@ export default {
332
332
  this.$emit('on-enter-click')
333
333
  this.$refs.inputElement.$el.blur()
334
334
  },
335
- onChangeHandler($event) {
336
- this.$emit('input-change', $event)
335
+ onChangeHandler(event) {
336
+ this.$emit('input-change', event.target.value)
337
337
  },
338
338
  onInputBlur($event) {
339
339
  this.$emit('input-blur', $event.target.value)
@@ -60,7 +60,7 @@
60
60
  // { label: 'Button 4', value: 'button_4', disabled: true }
61
61
  // ]
62
62
 
63
- import styled from 'vue-styled-components'
63
+ import styled from 'vue3-styled-components'
64
64
  import Modal from '../../modals/modal'
65
65
  import InfoText from '../../infoText'
66
66
 
@@ -1,6 +1,7 @@
1
1
  <template>
2
2
  <container :inputWidth="inputWidth">
3
- <input-wrapper>
3
+ <input-wrapper :iconPosition="iconPosition" :iconColor="iconColor">
4
+ <SearchIconSvg class="search-icn" />
4
5
  <input-container
5
6
  ref="inputElement"
6
7
  :placeholder="placeholder"
@@ -13,10 +14,6 @@
13
14
  :hasFocus="hasFocus"
14
15
  :data-id="dataId"
15
16
  />
16
- <img
17
- class="search-icn"
18
- :src="require('../../../assets/icons/search_icon_black.svg')"
19
- />
20
17
  </input-wrapper>
21
18
  </container>
22
19
  </template>
@@ -32,8 +29,11 @@
32
29
  // @on-change="function($event)"
33
30
  // :isFilter="true" // to set the height at 30px
34
31
  // data-id="test-data-id"
32
+ // iconPosition="left"
33
+ // iconColor="grey2"
35
34
  // />
36
- import styled from 'vue-styled-components'
35
+ import styled from 'vue3-styled-components'
36
+ import SearchIconSvg from '../../../assets/icons/search_icon_black.svg'
37
37
 
38
38
  const inputAttrs = {
39
39
  isDisabled: Boolean,
@@ -68,11 +68,18 @@ const InputContainer = styled('input', inputAttrs)`
68
68
  const InputWrapper = styled.span`
69
69
  position: relative;
70
70
 
71
- img {
71
+ svg {
72
72
  position: absolute;
73
- right: 10px;
74
- top: 50%;
75
73
  transform: translateY(-50%);
74
+ top: 50%;
75
+ ${(props) => (props.iconPosition === 'right' ? 'right' : 'left')}: 10px;
76
+
77
+ path {
78
+ fill: ${(props) =>
79
+ props.theme.colors[props.iconColor]
80
+ ? props.theme.colors[props.iconColor]
81
+ : props.iconColor};
82
+ }
76
83
  }
77
84
  `
78
85
 
@@ -81,7 +88,8 @@ export default {
81
88
  components: {
82
89
  InputContainer,
83
90
  InputWrapper,
84
- Container
91
+ Container,
92
+ SearchIconSvg
85
93
  },
86
94
  props: {
87
95
  value: {
@@ -110,11 +118,20 @@ export default {
110
118
  dataId: {
111
119
  required: false,
112
120
  default: ''
121
+ },
122
+ iconPosition: {
123
+ type: String,
124
+ default: 'right'
125
+ },
126
+ iconColor: {
127
+ type: String,
128
+ default: 'black'
113
129
  }
114
130
  },
131
+ emits: ['on-change'],
115
132
  methods: {
116
133
  onChangeHandler(event) {
117
- this.$emit('on-change', event)
134
+ this.$emit('on-change', event.target.value)
118
135
  },
119
136
  focusInputElement() {
120
137
  this.$nextTick(() => {
@@ -28,7 +28,9 @@
28
28
  <select-button-wrapper :disabled="disabled">
29
29
  <selectButton
30
30
  ref="select"
31
+ class="select-button"
31
32
  @click="toggleDropdown"
33
+ :selectWidth="selectWidth"
32
34
  :selectHeight="selectHeight"
33
35
  :height="height"
34
36
  :selectMinHeight="selectMinHeight"
@@ -39,9 +41,9 @@
39
41
  buttonFontColor || colorMode == 'dark' ? 'white' : 'black'
40
42
  "
41
43
  :hasError="hasError"
42
- :isSearchBarVisible="isSearchBarVisible"
44
+ :hasNoPadding="isSearchBarVisible || !hasSelectButtonPadding"
43
45
  :disabled="disabled"
44
- @keydown.native="onKeyDown"
46
+ @keydown="onKeyDown"
45
47
  :showBorder="showBorder"
46
48
  :data-id="dataId"
47
49
  :paddingLeft="paddingLeft"
@@ -64,7 +66,7 @@
64
66
  :value="textSearch"
65
67
  @keydown.stop="onKeyDown"
66
68
  @input-change="searchChange"
67
- @click.native.stop
69
+ @click.stop
68
70
  />
69
71
  <selector
70
72
  v-else
@@ -97,26 +99,33 @@
97
99
  />
98
100
  </Caret>
99
101
  </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>
102
+ <DropdownWrapper ref="dropdownWrapperRef">
103
+ <Teleport to="#portal-target">
104
+ <selectDropdown
105
+ ref="dropdown"
106
+ v-show="isSelectDropdownShown"
107
+ :dropdownPosition="dropdownPosition"
108
+ :hoveredIndex="hoveredIndex"
109
+ :hoveredValue="hoveredValue"
110
+ :isActive="isActive"
111
+ :optionWidth="getOptionWidth"
112
+ :hoveredBgColor="
113
+ colorMode == 'dark' ? '#000000' : dropdownBgColor
114
+ "
115
+ :bgColor="
116
+ dropdownBgColor || colorMode == 'dark' ? 'black' : 'white'
117
+ "
118
+ :fontColor="
119
+ dropdownFontColor || colorMode == 'dark' ? 'white' : 'black'
120
+ "
121
+ :fontSize="fontSize"
122
+ :selectedValue="selectedValue"
123
+ @option-selected="optionSelected"
124
+ @option-hovered="optionHovered"
125
+ >
126
+ <slot name="dropdown"></slot>
127
+ </selectDropdown>
128
+ </Teleport>
120
129
  </DropdownWrapper>
121
130
  </select-button-wrapper>
122
131
  </input-wrapper>
@@ -131,8 +140,9 @@
131
140
  // optionWidth="50%"
132
141
  // label="that is a label"
133
142
  // alignItems="vertical"
134
- // label-data-id="test-label0data-id"
143
+ // label-data-id="test-label-data-id"
135
144
  // data-id="test-data-id"
145
+ // :hasSelectButtonPadding="false"
136
146
  // >
137
147
  // <template #selector="{selectedValue}">
138
148
  // value selected: {{selectedValue}}
@@ -145,7 +155,8 @@
145
155
  // </template>
146
156
  // </Select>
147
157
 
148
- import styled from 'vue-styled-components'
158
+ import { Teleport } from 'vue'
159
+ import styled from 'vue3-styled-components'
149
160
  import InfoText from '../../infoText'
150
161
  import icon from '../../icon'
151
162
  import inputText from '../inputText'
@@ -161,7 +172,7 @@ const Caret = styled.div`
161
172
  width: ${CARET_WIDTH};
162
173
  min-width: ${CARET_WIDTH};
163
174
  height: 100%;
164
- align-items: stretch;
175
+ align-items: center;
165
176
  cursor: pointer;
166
177
  margin-left: auto;
167
178
  `
@@ -172,7 +183,10 @@ const selectorProps = {
172
183
  showBorder: Boolean
173
184
  }
174
185
  const Selector = styled('div', selectorProps)`
175
- ${(props) => props.selectWidth === '100%' ? 'width: 100%;' : `width: calc(${props.selectWidth} -
186
+ ${(props) =>
187
+ props.selectWidth === '100%'
188
+ ? 'width: 100%;'
189
+ : `width: calc(${props.selectWidth} -
176
190
  (
177
191
  ${CARET_WIDTH} +
178
192
  ${props.paddingLeft}
@@ -181,8 +195,7 @@ const Selector = styled('div', selectorProps)`
181
195
  );
182
196
  white-space: nowrap;
183
197
  text-overflow: ellipsis;
184
- overflow: hidden;`
185
- }
198
+ overflow: hidden;`}
186
199
  `
187
200
 
188
201
  const labelAttrs = { fontSize: String, fontColor: String }
@@ -197,7 +210,10 @@ const InputLabel = styled('div', labelAttrs)`
197
210
  const optionalLabel = styled.span`
198
211
  font-weight: 300;
199
212
  `
200
- const inputProps = { selectWidth: String, optionWidth: String }
213
+ const inputProps = {
214
+ selectWidth: String,
215
+ optionWidth: String
216
+ }
201
217
  const Container = styled('div', inputProps)`
202
218
  width: ${(props) => props.selectWidth};
203
219
  position: relative;
@@ -224,9 +240,10 @@ const selectButtonAttrs = {
224
240
  hasError: Boolean,
225
241
  disabled: Boolean,
226
242
  selectHeight: String,
243
+ selectWidth: String,
227
244
  height: String,
228
245
  selectMinHeight: String,
229
- isSearchBarVisible: Boolean,
246
+ hasNoPadding: Boolean,
230
247
  showBorder: Boolean,
231
248
  paddingLeft: String
232
249
  }
@@ -234,8 +251,9 @@ const selectButton = styled('div', selectButtonAttrs)`
234
251
  position: relative;
235
252
  box-sizing: border-box;
236
253
  border-radius: 4px;
254
+ max-width: ${(props) => (props.selectWidth ? props.selectWidth : '100%')};
237
255
  ${(props) =>
238
- props.isSearchBarVisible ? '' : `padding-left: ${props.paddingLeft}`};
256
+ props.hasNoPadding ? '' : `padding-left: ${props.paddingLeft}`};
239
257
  text-align: left;
240
258
  min-height: ${(props) =>
241
259
  props.selectHeight
@@ -247,7 +265,7 @@ const selectButton = styled('div', selectButtonAttrs)`
247
265
  : '36px'};
248
266
  display: flex;
249
267
  align-items: center;
250
- max-height: ${(props) => props.selectHeight};
268
+ height: ${(props) => props.selectHeight};
251
269
  ${({ showBorder, theme, hasError }) =>
252
270
  showBorder &&
253
271
  `
@@ -279,14 +297,17 @@ const selectDropdownAttrs = {
279
297
  fontColor: String,
280
298
  optionWidth: String,
281
299
  hoveredIndex: Number,
300
+ fontSize: String,
301
+ dropdownPosition: Object,
282
302
  hoveredValue: Number | String,
283
303
  selectedValue: Number | String
284
304
  }
285
305
  const selectDropdown = styled('div', selectDropdownAttrs)`
286
306
  box-sizing: border-box;
287
- z-index: ${(props) => (props.isActive ? '2' : '1')};
307
+ z-index: ${(props) => (props.isActive ? '2' : '99999')};
288
308
  position: absolute;
289
- top: 5px;
309
+ top: ${(props) => props.dropdownPosition?.top}px;
310
+ left: ${(props) => props.dropdownPosition?.left}px;
290
311
  border: ${BORDER_WIDTH} solid ${(props) => props.theme.colors.grey4};
291
312
  border-radius: 4px;
292
313
  display: flex;
@@ -311,7 +332,9 @@ const selectDropdown = styled('div', selectDropdownAttrs)`
311
332
  ? props.theme.colors[props.hoveredBgColor]
312
333
  : props.hoveredBgColor};
313
334
  }
335
+ font-size: ${(props) => props.fontSize};
314
336
  `
337
+ selectDropdown.emits = ['option-hovered', 'option-selected']
315
338
  const DropdownWrapper = styled('div')`
316
339
  position: relative;
317
340
  `
@@ -324,6 +347,10 @@ const InputWrapper = styled('div', inputAttrs)`
324
347
  grid-template-columns: ${(props) =>
325
348
  props.alignItems === 'vertical' || !props.hasLabel ? '1fr' : 'auto 1fr'};
326
349
  `
350
+
351
+ const DROPDOWN_HEIGHT_OFFSET = 4
352
+ const DROPDOWN_TOP_OFFSET = 21
353
+
327
354
  export default {
328
355
  name: 'RCselect',
329
356
 
@@ -435,6 +462,10 @@ export default {
435
462
  type: String,
436
463
  default: ''
437
464
  },
465
+ hasSelectButtonPadding: {
466
+ type: Boolean,
467
+ default: true
468
+ },
438
469
  isDraggable: {
439
470
  type: Boolean,
440
471
  default: false
@@ -456,6 +487,7 @@ export default {
456
487
  Caret,
457
488
  Selector,
458
489
  inputText,
490
+ Teleport,
459
491
  draggableInputHandle
460
492
  },
461
493
 
@@ -467,23 +499,38 @@ export default {
467
499
  isActive: false,
468
500
  textSearch: '',
469
501
  hoveredIndex: 0,
470
- hoveredValue: null,
471
- isClickOutsideActive: false
502
+ isClickOutsideActive: false,
503
+ dropdownPosition: {
504
+ left: null,
505
+ top: null
506
+ },
507
+ dropdownWidth: null,
508
+ hoveredValue: null
472
509
  }
473
510
  },
474
511
  mounted() {
512
+ this.observeDropdownHeight()
513
+ this.observeSelectWidth()
514
+ window.addEventListener('resize', this.handleSetDropdownOffet)
515
+ },
516
+ beforeMount() {
475
517
  this.selectedValue = this.value
476
518
  document.addEventListener('click', this.clickOutside)
519
+ this.getDropdownPosition()
520
+ window.removeEventListener('resize', this.handleSetDropdownOffet)
521
+ if (this.dropdownResizeObserver) this.dropdownResizeObserver.disconnect()
522
+ if (this.selectResizeObserver) this.selectResizeObserver.disconnect()
477
523
  },
478
- beforeDestroy() {
524
+ unmounted() {
479
525
  document.removeEventListener('click', this.clickOutside)
480
526
  },
481
527
  methods: {
482
528
  focus() {
483
529
  this.isActive = true
484
530
  },
485
- blur() {
531
+ blur(e) {
486
532
  this.isActive = false
533
+ this.$emit('blur', e)
487
534
  },
488
535
  toggleDropdown() {
489
536
  this.isDropdownOpen = !this.isDropdownOpen
@@ -499,6 +546,9 @@ export default {
499
546
  this.blur()
500
547
  this.isDropdownOpen = false
501
548
  },
549
+ clearSearch() {
550
+ this.textSearch = ''
551
+ },
502
552
  optionSelected(e) {
503
553
  this.selectedValue = e
504
554
  this.closeDropdown()
@@ -527,15 +577,15 @@ export default {
527
577
  searchChange(value) {
528
578
  this.textSearch = value
529
579
  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
- })
580
+ const dropdownChildren = [...this.$refs.dropdown.$el.children]
581
+ dropdownChildren.forEach((el) => {
582
+ if (!el.textContent.toLowerCase().includes(value.toLowerCase())) {
583
+ el.style.display = 'none'
584
+
585
+ return
586
+ }
587
+ el.style.display = 'inherit'
588
+ })
539
589
  },
540
590
  clickOutside(event) {
541
591
  const dropdownRef = this.$refs.dropdown
@@ -545,6 +595,7 @@ export default {
545
595
  if (
546
596
  this.$refs.select.$el == event.target ||
547
597
  this.$refs.select.$el.contains(event.target) ||
598
+ event.target.id === 'more-button' ||
548
599
  event.target.parentNode === dropdownRef.$el
549
600
  ) {
550
601
  return
@@ -558,13 +609,92 @@ export default {
558
609
  } else if (e.key == 'ArrowUp') {
559
610
  this.onArrowPress(-1)
560
611
  } else if (e.key == 'Enter') {
561
- const optionHoveredComponent =
562
- this.$refs.dropdown.$children[
563
- (this.hoveredIndex - 1 + this.optionLength) % this.optionLength
564
- ]
612
+ const optionHoveredComponent = [...this.$refs.dropdown.$el.children][
613
+ (this.hoveredIndex - 1 + this.optionLength) % this.optionLength
614
+ ]
565
615
  this.optionSelected(optionHoveredComponent.$el.dataset.value)
566
616
  }
567
617
  },
618
+ // If some part of the dropdown menu is outside viewport of the bottom of the screen,
619
+ // we need to offset it and display it at the top of the select dropdown instead
620
+ async getDropdownPosition() {
621
+ if (
622
+ !this.$refs.dropdownWrapperRef ||
623
+ !this.$refs.select ||
624
+ !this.$refs.dropdown
625
+ ) {
626
+ return
627
+ }
628
+ await this.$nextTick()
629
+ const isDisplayedAtBottom = await this.generateDropdownPosition()
630
+ await this.$nextTick()
631
+ // If the dropdown menu is going to be displayed at the bottom,
632
+ // we need reverify its position after a dom update (nextTick)
633
+ if (isDisplayedAtBottom) this.generateDropdownPosition()
634
+ },
635
+ async generateDropdownPosition() {
636
+ const isDropdownNotCompletelyVisible =
637
+ await this.isBottomOfDropdownOutOfViewport()
638
+ const dropdownWrapperEl = this.$refs.dropdownWrapperRef.$el
639
+ const selectButtonHeight = this.$refs.select.$el.clientHeight
640
+ const dropdownHeight = this.$refs.dropdown.$el.clientHeight
641
+ const dropdownWrapperRelativeHeight =
642
+ dropdownWrapperEl.getBoundingClientRect().top +
643
+ window.scrollY +
644
+ DROPDOWN_HEIGHT_OFFSET
645
+
646
+ const top = isDropdownNotCompletelyVisible
647
+ ? dropdownWrapperRelativeHeight
648
+ : dropdownWrapperRelativeHeight -
649
+ dropdownHeight -
650
+ selectButtonHeight -
651
+ DROPDOWN_TOP_OFFSET
652
+ const left = this.dropdownPosition.left
653
+ ? this.dropdownPosition.left
654
+ : dropdownWrapperEl.getBoundingClientRect().left + window.scrollX
655
+
656
+ this.dropdownPosition = { left: Math.floor(left), top: Math.floor(top) }
657
+
658
+ return isDropdownNotCompletelyVisible
659
+ },
660
+ async isBottomOfDropdownOutOfViewport() {
661
+ if (!this.$refs.dropdown) {
662
+ return false
663
+ }
664
+ await this.$nextTick()
665
+ const rect = this.$refs.dropdown.$el.getBoundingClientRect()
666
+ const windowHeight =
667
+ window.innerHeight || document.documentElement.clientHeight
668
+
669
+ // using Math.floor because the offsets may contain decimals we are not going to consider here
670
+ return Math.floor(rect.top) + Math.floor(rect.height) <= windowHeight
671
+ },
672
+ observeDropdownHeight() {
673
+ if (!this.$refs.dropdown) return
674
+ this.dropdownResizeObserver = new ResizeObserver(() => {
675
+ this.$nextTick(() => this.getDropdownPosition())
676
+ })
677
+ this.dropdownResizeObserver.observe(this.$refs.dropdown.$el)
678
+ },
679
+ handleSetDropdownOffet() {
680
+ if (!this.$refs.select) return
681
+ this.dropdownPosition.left = Math.floor(
682
+ this.$refs.select.$el.getBoundingClientRect().left
683
+ )
684
+ this.getDropdownWidth()
685
+ },
686
+ observeSelectWidth() {
687
+ if (!this.$refs.select) return
688
+ this.selectResizeObserver = new ResizeObserver(() =>
689
+ this.$nextTick(() => this.getDropdownWidth())
690
+ )
691
+ this.selectResizeObserver.observe(this.$refs.dropdown.$el)
692
+ },
693
+ async getDropdownWidth() {
694
+ if (!this.$refs.select) return
695
+ await this.$nextTick()
696
+ this.dropdownWidth = `${this.$refs.select.$el.clientWidth}px`
697
+ },
568
698
  onArrowPress(dir) {
569
699
  let newHoveredElem
570
700
  const currentHoveredElem = this.$refs.dropdown.$el.querySelector(
@@ -596,20 +726,34 @@ export default {
596
726
  return 0
597
727
  },
598
728
  isSearchBarVisible() {
599
- return this.isSearchable && this.isDropdownOpen
729
+ return this.isSearchable && this.optionLength >= 5 && this.isDropdownOpen
730
+ },
731
+ getOptionWidth() {
732
+ if (this.optionWidth) return this.optionWidth
733
+
734
+ return this.dropdownWidth
735
+ },
736
+ isSelectDropdownShown() {
737
+ return (
738
+ this.isDropdownOpen &&
739
+ this.dropdownPosition.left !== null &&
740
+ (!this.isSearchable || this.isSearchable)
741
+ )
600
742
  }
601
743
  },
602
744
  watch: {
603
745
  value(val) {
604
746
  this.selectedValue = val
605
747
  },
606
- isDropdownOpen(val) {
607
- this.$emit('is-dropdown-open', val)
748
+ async isDropdownOpen(val) {
608
749
  if (val) {
609
750
  setTimeout(() => {
610
751
  this.isClickOutsideActive = true
611
752
  }, 10)
753
+ await this.$nextTick()
754
+ this.handleSetDropdownOffet()
612
755
  } else {
756
+ this.dropdownPosition.left = null
613
757
  setTimeout(() => {
614
758
  this.isClickOutsideActive = false
615
759
  }, 10)