@eturnity/eturnity_reusable_components 7.24.3-EPDM-10647.1 → 7.24.3-EPDM-11320.2

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