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