@eturnity/eturnity_reusable_components 7.24.3-EPDM-11143.1 → 7.24.3-EPDM-11320.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 (82) hide show
  1. package/.eslintrc.js +184 -0
  2. package/.prettierrc +8 -6
  3. package/package.json +9 -21
  4. package/src/App.vue +79 -78
  5. package/src/assets/theme.js +3 -3
  6. package/src/components/addNewButton/AddNewButton.stories.js +2 -2
  7. package/src/components/addNewButton/index.vue +48 -51
  8. package/src/components/banner/actionBanner/index.vue +54 -55
  9. package/src/components/banner/banner/banner.stories.js +5 -5
  10. package/src/components/banner/banner/index.vue +159 -159
  11. package/src/components/banner/infoBanner/index.vue +41 -53
  12. package/src/components/buttons/buttonIcon/index.vue +125 -122
  13. package/src/components/buttons/closeButton/CloseButton.stories.js +3 -3
  14. package/src/components/buttons/closeButton/index.vue +49 -49
  15. package/src/components/buttons/mainButton/index.vue +119 -119
  16. package/src/components/card/index.vue +70 -70
  17. package/src/components/collapsableInfoText/index.vue +96 -94
  18. package/src/components/deleteIcon/DeleteIcon.stories.js +4 -4
  19. package/src/components/deleteIcon/index.vue +54 -54
  20. package/src/components/draggableInputHandle/index.vue +37 -37
  21. package/src/components/dropdown/Dropdown.stories.js +9 -10
  22. package/src/components/dropdown/index.vue +106 -106
  23. package/src/components/errorMessage/index.vue +52 -52
  24. package/src/components/filter/filterSettings.vue +439 -433
  25. package/src/components/filter/index.vue +135 -135
  26. package/src/components/filter/parentDropdown.vue +73 -73
  27. package/src/components/icon/Icons.stories.js +7 -7
  28. package/src/components/icon/iconCollection.vue +53 -53
  29. package/src/components/icon/index.vue +121 -121
  30. package/src/components/iconWrapper/index.vue +156 -156
  31. package/src/components/infoCard/index.vue +26 -26
  32. package/src/components/infoText/index.vue +133 -132
  33. package/src/components/inputs/checkbox/Checkbox.stories.js +8 -8
  34. package/src/components/inputs/checkbox/index.vue +190 -180
  35. package/src/components/inputs/inputNumber/InputNumber.stories.js +41 -41
  36. package/src/components/inputs/inputNumber/index.vue +647 -644
  37. package/src/components/inputs/inputNumberQuestion/index.vue +185 -182
  38. package/src/components/inputs/inputText/InputText.stories.js +22 -22
  39. package/src/components/inputs/inputText/index.vue +337 -336
  40. package/src/components/inputs/radioButton/RadioButton.stories.js +16 -16
  41. package/src/components/inputs/radioButton/index.vue +221 -219
  42. package/src/components/inputs/searchInput/SearchInput.stories.js +8 -8
  43. package/src/components/inputs/searchInput/index.vue +126 -126
  44. package/src/components/inputs/select/index.vue +778 -776
  45. package/src/components/inputs/select/option/index.vue +124 -124
  46. package/src/components/inputs/select/select.stories.js +31 -32
  47. package/src/components/inputs/slider/index.vue +99 -99
  48. package/src/components/inputs/switchField/index.vue +220 -222
  49. package/src/components/inputs/textAreaInput/TextAreaInput.stories.js +57 -57
  50. package/src/components/inputs/textAreaInput/index.vue +171 -173
  51. package/src/components/inputs/toggle/Toggle.stories.js +14 -14
  52. package/src/components/inputs/toggle/index.vue +214 -217
  53. package/src/components/label/index.vue +82 -82
  54. package/src/components/markerItem/index.vue +68 -66
  55. package/src/components/modals/actionModal/index.vue +54 -54
  56. package/src/components/modals/infoModal/index.vue +39 -36
  57. package/src/components/modals/modal/index.vue +134 -134
  58. package/src/components/modals/modal/modal.stories.js +5 -5
  59. package/src/components/navigationTabs/index.vue +96 -94
  60. package/src/components/pageSubtitle/index.vue +55 -49
  61. package/src/components/pageTitle/index.vue +56 -56
  62. package/src/components/pagination/index.vue +92 -89
  63. package/src/components/progressBar/index.vue +107 -107
  64. package/src/components/projectMarker/index.vue +246 -244
  65. package/src/components/rangeSlider/Slider.vue +491 -465
  66. package/src/components/rangeSlider/index.vue +410 -410
  67. package/src/components/rangeSlider/utils/dom.js +5 -5
  68. package/src/components/selectedOptions/index.vue +119 -119
  69. package/src/components/sideMenu/index.vue +199 -199
  70. package/src/components/spinner/index.vue +57 -57
  71. package/src/components/tableDropdown/index.vue +520 -520
  72. package/src/components/tables/mainTable/index.vue +366 -362
  73. package/src/components/tables/viewTable/index.vue +171 -171
  74. package/src/components/threeDots/index.vue +340 -334
  75. package/src/components/videoThumbnail/index.vue +86 -86
  76. package/src/components/videoThumbnail/videoThumbnail.stories.js +14 -16
  77. package/src/helpers/numberConverter.js +2 -2
  78. package/src/helpers/translateLang.js +9 -9
  79. package/src/mixins/inputValidations.js +5 -5
  80. package/public/favicon.ico +0 -0
  81. package/public/index.html +0 -17
  82. package/src/assets/svgIcons/copy.svg +0 -10
@@ -1,203 +1,204 @@
1
1
  <template>
2
2
  <Container
3
- :selectWidth="selectWidth"
4
- :noRelative="noRelative"
3
+ :no-relative="noRelative"
4
+ :select-width="selectWidth"
5
5
  @mouseenter="mouseEnterHandler"
6
6
  @mouseleave="mouseLeaveHandler"
7
7
  >
8
- <input-wrapper
9
- :hasLabel="!!label && label.length > 0"
10
- :alignItems="alignItems"
11
- :noRelative="noRelative"
8
+ <InputWrapper
9
+ :align-items="alignItems"
10
+ :has-label="!!label && label.length > 0"
11
+ :no-relative="noRelative"
12
12
  >
13
- <label-wrapper v-if="label" :data-id="labelDataId">
14
- <input-label
15
- :fontColor="
13
+ <LabelWrapper v-if="label" :data-id="labelDataId">
14
+ <InputLabel
15
+ :font-color="
16
16
  labelFontColor || colorMode == 'dark' ? 'white' : 'eturnityGrey'
17
17
  "
18
- :fontSize="fontSize"
19
- >{{ label }}
20
- <optionalLabel v-if="labelOptional">
21
- ({{ $gettext('Optional') }})</optionalLabel
22
- >
23
- </input-label>
24
- <info-text
18
+ :font-size="fontSize"
19
+ >
20
+ {{ label }}
21
+ <OptionalLabel v-if="labelOptional">
22
+ ({{ $gettext('Optional') }})
23
+ </OptionalLabel>
24
+ </InputLabel>
25
+ <InfoText
25
26
  v-if="infoTextMessage"
26
- :text="infoTextMessage"
27
27
  :size="infoTextSize"
28
+ :text="infoTextMessage"
28
29
  />
29
- </label-wrapper>
30
- <select-button-wrapper :disabled="disabled">
31
- <selectButton
30
+ </LabelWrapper>
31
+ <SelectButtonWrapper :disabled="disabled">
32
+ <SelectButton
32
33
  ref="select"
33
- class="select-button"
34
- @click="toggleDropdown"
35
- :selectWidth="selectWidth"
36
- :selectHeight="selectHeight"
37
- :height="height"
38
- :selectMinHeight="selectMinHeight"
39
- :bgColor="
34
+ :bg-color="
40
35
  buttonBgColor || colorMode == 'dark' ? 'transparentBlack1' : 'white'
41
36
  "
42
- :fontColor="
37
+ class="select-button"
38
+ :data-id="dataId"
39
+ :disabled="disabled"
40
+ :font-color="
43
41
  buttonFontColor || colorMode == 'dark' ? 'white' : 'black'
44
42
  "
45
- :fontSize="fontSize"
46
- :hasError="hasError"
47
- :hasNoPadding="isSearchBarVisible || !hasSelectButtonPadding"
48
- :disabled="disabled"
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"
49
56
  @keydown="onKeyDown"
50
- :showBorder="showBorder"
51
- :data-id="dataId"
52
- :paddingLeft="paddingLeft"
53
- :tablePaddingLeft="tablePaddingLeft"
54
- :noRelative="noRelative"
55
- :showDisabledBackground="showDisabledBackground"
56
57
  >
57
- <draggableInputHandle
58
+ <DraggableInputHandle
58
59
  v-if="isDraggable && !isSearchBarVisible"
59
60
  :height="selectHeight"
60
61
  />
61
- <inputText
62
+ <InputText
62
63
  v-if="isSearchBarVisible"
63
64
  ref="searchInput"
64
- tabindex="0"
65
- inputHeight="34px"
66
- :noBorder="true"
67
- :fontSize="fontSize"
68
- backgroundColor="transparent"
69
- :fontColor="
65
+ background-color="transparent"
66
+ :font-color="
70
67
  buttonFontColor || colorMode == 'dark' ? 'white' : 'black'
71
68
  "
69
+ :font-size="fontSize"
70
+ input-height="34px"
71
+ :input-width="computedWidth"
72
+ :no-border="true"
73
+ tabindex="0"
72
74
  :value="textSearch"
73
- :inputWidth="computedWidth"
74
- @keydown.stop="onKeyDown"
75
- @input-change="searchChange"
76
75
  @click.stop
76
+ @input-change="searchChange"
77
+ @keydown.stop="onKeyDown"
77
78
  />
78
- <selector
79
+ <Selector
79
80
  v-else
80
- :showBorder="showBorder"
81
- :selectWidth="selectWidth"
82
- :paddingLeft="paddingLeft"
81
+ :padding-left="paddingLeft"
82
+ :select-width="selectWidth"
83
+ :show-border="showBorder"
83
84
  >
84
- <slot name="selector" :selectedValue="selectedValue"></slot>
85
- </selector>
86
- <Caret @click.stop="toggleCaretDropdown" class="caret_dropdown">
87
- <icon
85
+ <slot name="selector" :selected-value="selectedValue"></slot>
86
+ </Selector>
87
+ <Caret class="caret_dropdown" @click.stop="toggleCaretDropdown">
88
+ <RCIcon
88
89
  v-if="isDropdownOpen"
89
- name="arrow_up"
90
- size="12px"
91
90
  :color="
92
91
  caretColor || colorMode == 'dark'
93
92
  ? 'white'
94
93
  : 'transparentBlack1'
95
94
  "
95
+ name="arrow_up"
96
+ size="12px"
96
97
  />
97
- <icon
98
+ <RCIcon
98
99
  v-else
99
- name="arrow_down"
100
- size="12px"
101
100
  :color="
102
101
  caretColor || colorMode == 'dark'
103
102
  ? 'white'
104
103
  : 'transparentBlack1'
105
104
  "
105
+ name="arrow_down"
106
+ size="12px"
106
107
  />
107
108
  </Caret>
108
- </selectButton>
109
- <DropdownWrapper ref="dropdownWrapperRef" :noRelative="noRelative">
109
+ </SelectButton>
110
+ <DropdownWrapper ref="dropdownWrapperRef" :no-relative="noRelative">
110
111
  <Teleport to="#portal-target">
111
- <selectDropdown
112
- ref="dropdown"
112
+ <SelectDropdown
113
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="
114
+ ref="dropdown"
115
+ :bg-color="
123
116
  dropdownBgColor || colorMode == 'dark' ? 'black' : 'white'
124
117
  "
125
- :fontColor="
118
+ :dropdown-position="dropdownPosition"
119
+ :font-color="
126
120
  dropdownFontColor || colorMode == 'dark' ? 'white' : 'black'
127
121
  "
128
- :noRelative="noRelative"
129
- :fontSize="fontSize"
130
- :minWidth="minWidth"
131
- :selectedValue="selectedValue"
132
- @option-selected="optionSelected"
133
- @option-hovered="optionHovered"
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"
134
133
  @mouseleave="optionLeave"
134
+ @option-hovered="optionHovered"
135
+ @option-selected="optionSelected"
135
136
  >
136
137
  <slot name="dropdown"></slot>
137
- </selectDropdown>
138
+ </SelectDropdown>
138
139
  </Teleport>
139
140
  </DropdownWrapper>
140
- </select-button-wrapper>
141
- </input-wrapper>
141
+ </SelectButtonWrapper>
142
+ </InputWrapper>
142
143
  </Container>
143
144
  </template>
144
145
 
145
146
  <script>
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>
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>
168
169
 
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'
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'
175
176
 
176
- const CARET_WIDTH = '30px'
177
- const BORDER_WIDTH = '1px'
177
+ const CARET_WIDTH = '30px'
178
+ const BORDER_WIDTH = '1px'
178
179
 
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
- `
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
+ `
190
191
 
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} -
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} -
201
202
  (
202
203
  ${CARET_WIDTH} +
203
204
  ${props.paddingLeft}
@@ -207,679 +208,680 @@ const Selector = styled('div', selectorProps)`
207
208
  white-space: nowrap;
208
209
  text-overflow: ellipsis;
209
210
  overflow: hidden;`}
210
- `
211
+ `
211
212
 
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
- `
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
+ `
241
242
 
242
- const SelectButtonWrapperAttrs = {
243
- disabled: Boolean
244
- }
245
- const SelectButtonWrapper = styled('div', SelectButtonWrapperAttrs)`
246
- ${(props) => (props.disabled ? 'cursor: not-allowed' : 'cursor: pointer')};
247
- `
243
+ const SelectButtonWrapperAttrs = {
244
+ disabled: Boolean,
245
+ }
246
+ const SelectButtonWrapper = styled('div', SelectButtonWrapperAttrs)`
247
+ ${(props) => (props.disabled ? 'cursor: not-allowed' : 'cursor: pointer')};
248
+ `
248
249
 
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
- `
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
+ `
295
296
  border: ${BORDER_WIDTH} solid ${
296
- hasError ? theme.colors.red : theme.colors.grey4
297
- }
297
+ hasError ? theme.colors.red : theme.colors.grey4
298
+ }
298
299
  `}
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;
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;
316
+ }
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,
315
330
  }
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}'] {
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%'};
362
352
  background-color: ${(props) =>
363
- props.theme.colors[props.hoveredBgColor]
364
- ? props.theme.colors[props.hoveredBgColor]
365
- : props.hoveredBgColor};
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,
366
379
  }
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
- `
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
+ `
389
390
 
390
- const DROPDOWN_HEIGHT_OFFSET = 4
391
- const DROPDOWN_TOP_OFFSET = 21
392
- const MIN_OPTION_LENGTH = 5
391
+ const DROPDOWN_HEIGHT_OFFSET = 4
392
+ const DROPDOWN_TOP_OFFSET = 21
393
+ const MIN_OPTION_LENGTH = 5
393
394
 
394
- const DROPDOWN_MENU_POSITIONS = {
395
- Automatic: 'automatic',
396
- Bottom: 'bottom'
397
- }
395
+ const DROPDOWN_MENU_POSITIONS = {
396
+ Automatic: 'automatic',
397
+ Bottom: 'bottom',
398
+ }
398
399
 
399
- export default {
400
- name: 'RCselect',
400
+ export default {
401
+ name: 'RCselect',
401
402
 
402
- props: {
403
- value: {
404
- required: false,
405
- default: null
406
- },
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'
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,
452
420
  },
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
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
- },
552
-
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
421
 
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
641
- }
642
- },
643
- mouseLeaveHandler() {
644
- if (this.hoverDropdown) {
645
- this.blur()
646
- }
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
+ },
647
571
  },
648
- optionLeave() {
649
- if (this.dropdownAutoClose) {
650
- this.isDropdownOpen = false
572
+
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,
651
588
  }
652
589
  },
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'
660
-
661
- return
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
662
596
  }
663
597
 
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
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', ''))
610
+ }
722
611
 
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
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
735
618
 
736
- this.dropdownPosition = { left: Math.floor(left), top: Math.floor(top) }
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
737
635
 
738
- return isDropdownNotCompletelyVisible
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
+ },
739
643
  },
740
- async isBottomOfDropdownOutOfViewport() {
741
- if (
742
- !this.$refs.dropdown ||
743
- this.dropdownMenuPosition === DROPDOWN_MENU_POSITIONS.Bottom
744
- ) {
745
- return false
746
- }
747
-
748
- await this.$nextTick()
749
- const rect = this.$refs.dropdown.$el.getBoundingClientRect()
750
- const windowHeight =
751
- window.innerHeight || document.documentElement.clientHeight
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
+ },
670
+ },
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'
752
742
 
753
- if (windowHeight <= 650) return true
743
+ return
744
+ }
754
745
 
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
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
793
761
  } else {
794
- newHoveredElem = currentHoveredElem.previousElementSibling
762
+ this.closeDropdown()
795
763
  }
796
- if (newHoveredElem) {
797
- this.hoveredValue = newHoveredElem.getAttribute('data-value')
798
- const topPos = newHoveredElem.offsetTop
799
- this.$refs.dropdown.$el.scrollTop = topPos
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)
800
775
  }
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
- }
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
786
+ }
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
811
804
 
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
- }
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
825
817
 
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
818
+ this.dropdownPosition = { left: Math.floor(left), top: Math.floor(top) }
832
819
 
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
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
+ }
848
829
 
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)
867
830
  await this.$nextTick()
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()
879
- }
831
+ const rect = this.$refs.dropdown.$el.getBoundingClientRect()
832
+ const windowHeight =
833
+ window.innerHeight || document.documentElement.clientHeight
834
+
835
+ if (windowHeight <= 650) return true
836
+
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())
880
844
  })
881
- }
882
- }
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
864
+ 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
882
+ }
883
+ }
884
+ },
885
+ },
883
886
  }
884
- }
885
887
  </script>