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