@eturnity/eturnity_reusable_components 7.30.3 → 7.32.0

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