@itfin/components 1.0.74 → 1.0.78

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@itfin/components",
3
- "version": "1.0.74",
3
+ "version": "1.0.78",
4
4
  "main": "dist/itfin-components.umd.js",
5
5
  "unpkg": "dist/itfin-components.common.js",
6
6
  "author": "Vitalii Savchuk <esvit666@gmail.com>",
@@ -1,10 +1,12 @@
1
1
  <template>
2
2
 
3
3
  <div class="itf-label" :class="{'has-validation': hasState, 'no-details': hideDetails}">
4
- <label class="itf-label__label form-label" v-if="label">
5
- {{label}}
6
- <span class="star" v-if="required">*</span>
7
- </label>
4
+ <slot name="label" v-bind="{ required, label }">
5
+ <label class="itf-label__label form-label" v-if="label">
6
+ {{label}}
7
+ <span class="star" v-if="required">*</span>
8
+ </label>
9
+ </slot>
8
10
 
9
11
  <div :class="{'is-invalid': hasState && hasError, 'is-valid': hasState && hasSuccess}">
10
12
  <slot :validate="doValidation"></slot>
@@ -41,6 +41,7 @@
41
41
 
42
42
  @supports ((-webkit-backdrop-filter: none) or (backdrop-filter: none)) {
43
43
  backdrop-filter: blur(5px);
44
+ -webkit-backdrop-filter: blur(5px);
44
45
  background-color: rgba(255, 255, 255, .5);
45
46
  }
46
47
  }
@@ -0,0 +1,361 @@
1
+ <template>
2
+ <div
3
+ class="select"
4
+ ref="selectRef"
5
+ :class="[variant]"
6
+ @keydown="handleFocusedSelectKeydown"
7
+ >
8
+ <div
9
+ class="valueContainer text-textDarkest"
10
+ :data-testid="name ? `select:${name}` : 'select'"
11
+ @click="activateDropdown"
12
+ >
13
+ <div class="placeholder" v-if="isValueEmpty">{{ placeholder }}</div>
14
+
15
+ <slot
16
+ v-if="!isValueEmpty && !multiple && customRender"
17
+ v-bind="getOption(localValue)"
18
+ />
19
+ <template v-else>
20
+ {{ getOptionLabel(localValue) }}
21
+ </template>
22
+
23
+ <div v-if="!isValueEmpty && multiple" class="valueMulti text-textDarkest">
24
+ <div
25
+ class="my-1 mx-1 flex items-center"
26
+ v-for="optionValue in localValue"
27
+ :key="optionValue"
28
+ >
29
+ <slot
30
+ v-if="customRender"
31
+ v-bind="{
32
+ ...getOption(optionValue),
33
+ optionValue,
34
+ remove: removeOptionValue
35
+ }"
36
+ />
37
+ <div v-else class="valueMultiItem text-textDarkest" :style="getOptionColor(optionValue) ? `background-color: #${getOptionColor(optionValue)}; color: #fff` : ''">
38
+ <div class="valueMultiItemLabel">
39
+ {{ getOptionLabel(optionValue) }}
40
+ </div>
41
+ <div
42
+ @click="removeOptionValue(optionValue)"
43
+ class="valueMultiItemClose"
44
+ >
45
+ <svg class="icon" viewBox="0 0 24 24">
46
+ <path
47
+ d="M16.192 6.344L11.949 10.586 7.707 6.344 6.293 7.758 10.535 12 6.293 16.242 7.707 17.656 11.949 13.414 16.192 17.656 17.606 16.242 13.364 12 17.606 7.758z"
48
+ />
49
+ </svg>
50
+ </div>
51
+ </div>
52
+ </div>
53
+
54
+ <div class="addMore m-1">
55
+ <svg
56
+ class="icon"
57
+ viewBox="0 0 24 24"
58
+ focusable="false"
59
+ role="presentation"
60
+ >
61
+ <path
62
+ d="M13 11V3.993A.997.997 0 0 0 12 3c-.556 0-1 .445-1 .993V11H3.993A.997.997 0 0 0 3 12c0 .557.445 1 .993 1H11v7.007c0 .548.448.993 1 .993.556 0 1-.445 1-.993V13h7.007A.997.997 0 0 0 21 12c0-.556-.445-1-.993-1H13z"
63
+ fill="currentColor"
64
+ fill-rule="evenodd"
65
+ ></path>
66
+ </svg>
67
+ <div class="font-medium">Add more</div>
68
+ </div>
69
+ </div>
70
+
71
+ <j-icon
72
+ v-if="(!multiple || isValueEmpty) && variant !== 'empty'"
73
+ class="ml-auto mr-1"
74
+ name="chevron-down"
75
+ ></j-icon>
76
+ </div>
77
+
78
+ <Dropdown
79
+ v-if="isDropdownOpen"
80
+ ref="dropdownRef"
81
+ isValueEmpty
82
+ :dropdownWidth="dropdownWidth"
83
+ :searchable="searchable"
84
+ :searchValue="searchValue"
85
+ :value="localValue"
86
+ :deactivateDropdown="deactivateDropdown"
87
+ :options="options"
88
+ :multiple="multiple"
89
+ :item-key="itemKey"
90
+ :item-text="itemText"
91
+ :withClearValue="withClearValue"
92
+ @change="handleChange"
93
+ @searchValueChange="handleSearchValueChange"
94
+ >
95
+ <template v-slot:option="props">
96
+ <slot v-if="customRenderOption" name="option" v-bind="props" />
97
+ <slot v-else v-bind="props" />
98
+ </template>
99
+ </Dropdown>
100
+ </div>
101
+ </template>
102
+
103
+ <script>
104
+ import { Component, Model, Prop, Ref } from 'vue-property-decorator';
105
+ import Vue from 'vue';
106
+ import Dropdown from './Dropdown.vue';
107
+ import { useOutsideClick } from './useOutsideClick';
108
+
109
+ const { bind, unbind } = useOutsideClick();
110
+
111
+ export default @Component({
112
+ name: 'itfAirSelect',
113
+ components: { Dropdown },
114
+ })
115
+ class itfAirSelect extends Vue {
116
+ @Model('input', { type: [Array, String, Number], default: undefined }) value;
117
+
118
+ @Prop({ type: Number, default: undefined }) dropdownWidth;
119
+
120
+ @Prop({ type: String, default: 'normal' }) variant;
121
+
122
+ @Prop({ type: String, default: 'value' }) itemKey;
123
+ @Prop({ type: String, default: 'label' }) itemText;
124
+ @Prop({ type: String, default: 'color' }) itemColor;
125
+
126
+ @Prop({ type: String, default: undefined }) name;
127
+
128
+ @Prop({ type: Boolean, default: false }) searchable;
129
+
130
+ @Prop({ type: [String, Array, Number], default: undefined }) defaultValue;
131
+
132
+ @Prop({ type: String, default: 'Select' }) placeholder;
133
+
134
+ @Prop({ type: Boolean, default: false }) invalid;
135
+
136
+ @Prop({ type: Array, required: true }) options;
137
+
138
+ @Prop({ type: Boolean, default: false }) multiple;
139
+
140
+ @Prop({ type: Boolean, default: false }) withClearValue;
141
+
142
+ @Ref('selectRef') selectRef;
143
+
144
+ @Ref('dropdownRef') dropdownRef;
145
+
146
+ isDropdownOpen = false;
147
+
148
+ searchValue = '';
149
+
150
+ get customRender() {
151
+ return !!this.$slots.default;
152
+ }
153
+
154
+ get customRenderOption() {
155
+ return !!this.$slots.option;
156
+ }
157
+
158
+ data() {
159
+ return {
160
+ stateValue: this.defaultValue || (this.multiple ? [] : null),
161
+ };
162
+ }
163
+
164
+ getOption(optionValue) {
165
+ return this.options.find((option) => option[this.itemKey] === optionValue);
166
+ }
167
+
168
+ getOptionLabel(optionValue) {
169
+ return (this.getOption(optionValue) || { [this.itemText]: '' })[this.itemText];
170
+ }
171
+
172
+ getOptionColor(optionValue) {
173
+ console.info((this.getOption(optionValue) || { [this.itemColor]: '' })[this.itemColor])
174
+ return (this.getOption(optionValue) || { [this.itemColor]: '' })[this.itemColor];
175
+ }
176
+
177
+ get isControlled() {
178
+ return this.value !== undefined;
179
+ }
180
+
181
+ get localValue() {
182
+ return this.isControlled ? this.value : this.stateValue;
183
+ }
184
+
185
+ preserveValueType(newValue) {
186
+ const areOptionValuesNumbers = this.options.some(
187
+ (option) => typeof option[this.itemKey] === 'number',
188
+ );
189
+ if (areOptionValuesNumbers) {
190
+ if (this.multiple) {
191
+ return (newValue).map(Number);
192
+ }
193
+ if (newValue) {
194
+ return Number(newValue);
195
+ }
196
+ }
197
+ return newValue;
198
+ }
199
+
200
+ handleChange(newValue) {
201
+ if (!this.isControlled) {
202
+ this.stateValue = this.preserveValueType(newValue);
203
+ }
204
+ this.$emit('input', this.preserveValueType(newValue));
205
+ }
206
+
207
+ removeOptionValue(optionValue) {
208
+ this.handleChange(this.localValue.filter((val) => val !== optionValue));
209
+ }
210
+
211
+ get isValueEmpty() {
212
+ return this.multiple ? !this.localValue.length : !this.getOption(this.localValue);
213
+ }
214
+
215
+ handleFocusedSelectKeydown(event) {
216
+ if (this.isDropdownOpen) return;
217
+ if (event.keyCode === 13) {
218
+ event.preventDefault();
219
+ }
220
+ if (event.keyCode !== 27 && event.keyCode !== 9 && !event.shiftKey) {
221
+ this.isDropdownOpen = true;
222
+ }
223
+ }
224
+
225
+ async deactivateDropdown() {
226
+ this.isDropdownOpen = false;
227
+ this.searchValue = '';
228
+ await this.$nextTick();
229
+ this.selectRef && this.selectRef.focus();
230
+ }
231
+
232
+ async activateDropdown() {
233
+ this.isDropdownOpen = true;
234
+ await this.$nextTick();
235
+ this.selectRef && this.selectRef.blur();
236
+ // eslint-disable-next-line
237
+ this.searchable && this.dropdownRef && this.dropdownRef.$refs.inputRef.focus()
238
+ }
239
+
240
+ handleSearchValueChange(newValue) {
241
+ this.searchValue = newValue;
242
+ }
243
+
244
+ mounted () {
245
+ bind(this.selectRef, this.deactivateDropdown);
246
+ }
247
+
248
+ beforeDestroy () {
249
+ unbind();
250
+ }
251
+ }
252
+ </script>
253
+
254
+ <style lang="scss" scoped>
255
+ .select {
256
+ position: relative;
257
+ border-radius: 4px;
258
+ cursor: pointer;
259
+ font-size: 14px;
260
+ transition: background 0.1s;
261
+ &:focus {
262
+ outline: none;
263
+ }
264
+ }
265
+ .valueContainer {
266
+ display: flex;
267
+ align-items: center;
268
+ width: 100%;
269
+ min-height: 32px;
270
+ }
271
+ .placeholder {
272
+ color: #8993a4;
273
+ }
274
+ .valueMulti {
275
+ display: flex;
276
+ align-items: center;
277
+ flex-wrap: wrap;
278
+ // padding-top: 5px;
279
+ }
280
+ .valueMultiItem {
281
+ height: 22px;
282
+ overflow: hidden;
283
+ border-radius: 3px;
284
+ display: flex;
285
+ align-items: center;
286
+ user-select: none;
287
+ background: #f0f0f0;
288
+ font-size: 14px;
289
+ .valueMultiItemLabel {
290
+ flex: auto;
291
+ padding: 0 4px;
292
+ height: 100%;
293
+ display: flex;
294
+ align-items: center;
295
+ border-right: 1px solid #ddd;
296
+ }
297
+ .valueMultiItemClose {
298
+ border-left: none;
299
+ overflow: hidden;
300
+ padding: 0 2px;
301
+ height: 100%;
302
+ display: flex;
303
+ align-items: center;
304
+ justify-content: center;
305
+ &:hover {
306
+ background: #ff4757;
307
+ color: white;
308
+ }
309
+ .icon {
310
+ width: 18px;
311
+ height: 18px;
312
+ fill: currentColor;
313
+ }
314
+ }
315
+ }
316
+ .addMore {
317
+ font-size: 12.5px;
318
+ cursor: pointer;
319
+ display: flex;
320
+ align-items: center;
321
+ color: #0052cc;
322
+ &:hover,
323
+ &:visited,
324
+ &:active {
325
+ color: #0052cc;
326
+ }
327
+ &:hover {
328
+ text-decoration: underline;
329
+ }
330
+ .icon {
331
+ width: 16px;
332
+ height: 16px;
333
+ fill: currentColor;
334
+ margin-right: 2px;
335
+ }
336
+ }
337
+ .select.normal {
338
+ width: 100%;
339
+ padding-left: 0.5rem;
340
+ padding-right: 0.5rem;
341
+ border-width: 1px;
342
+ border-color: #dfe1e6;
343
+ background-color: #f4f5f7;
344
+ position: relative;
345
+ border-radius: 4px;
346
+ cursor: pointer;
347
+ font-size: 14px;
348
+ -webkit-transition: background .1s;
349
+ transition: background .1s;
350
+ }
351
+ .select.normal:hover {
352
+ @apply bg-backgroundLight;
353
+ }
354
+ .select.normal:focus {
355
+ @apply border border-borderInputFocus bg-white text-borderInputFocus;
356
+ box-shadow: 0 0 0 1px currentColor;
357
+ }
358
+ .select.empty {
359
+ display: inline-block;
360
+ }
361
+ </style>
@@ -0,0 +1,277 @@
1
+ <template>
2
+ <div class="dropdown" :style="{ width: `${dropdownWidth}px` }">
3
+ <input
4
+ v-if="searchable"
5
+ class="dropdownInput"
6
+ type="text"
7
+ ref="inputRef"
8
+ placeholder="Search"
9
+ @input="handleSearchValueChange"
10
+ @keydown="handleInputKeyDown"
11
+ />
12
+
13
+ <div class="options" ref="optionsRef">
14
+ <div
15
+ class="option text-textDarkest"
16
+ v-for="option in filteredOptions"
17
+ :key="option[itemKey]"
18
+ :data-select-option-value="option[itemKey]"
19
+ :data-testid="`select-option:${option[itemText]}`"
20
+ @mouseenter="handleOptionMouseEnter"
21
+ @click="selectOptionValue(option[itemKey])"
22
+ >
23
+ <slot name="option" v-bind="option">{{ option[itemText] }}</slot>
24
+ </div>
25
+
26
+ <div
27
+ v-if="isOptionCreatable"
28
+ class="option text-textDarkest"
29
+ :data-create-option-label="{ searchValue }"
30
+ @mouseenter="handleOptionMouseEnter"
31
+ @click="createOption(searchValue)"
32
+ >
33
+ {{
34
+ isCreatingOption
35
+ ? `Creating "${searchValue}"...`
36
+ : `Create
37
+ "${searchValue}"`
38
+ }}
39
+ </div>
40
+ </div>
41
+
42
+ <div v-if="filteredOptions.length === 0" class="optionsNoResults">
43
+ No results
44
+ </div>
45
+ </div>
46
+ </template>
47
+
48
+ <script>
49
+ import Vue from 'vue';
50
+ import { Component, Prop, Ref } from 'vue-property-decorator';
51
+
52
+ const activeOptionClass = 'select-option-is-active';
53
+
54
+ export default @Component({
55
+ name: 'itfDropdown',
56
+ components: {}
57
+ })
58
+ class itfSegmentedControl extends Vue {
59
+ @Prop({ type: Number, default: undefined }) dropdownWidth;
60
+ @Prop({ type: [Array, String, Number], default: undefined }) value;
61
+ @Prop({ type: Boolean }) isValueEmpty;
62
+ @Prop({ type: Boolean }) searchable;
63
+ @Prop({ type: String, default: 'value' }) itemKey;
64
+ @Prop({ type: String, default: 'label' }) itemText;
65
+ @Prop({ type: String, default: '' }) searchValue;
66
+ @Prop({ type: Function, required: true }) deactivateDropdown;
67
+ @Prop({ type: Array, required: true }) options;
68
+ @Prop({ type: Function, default: undefined }) onCreate;
69
+ @Prop({ type: Boolean }) multiple;
70
+ @Prop({ type: Boolean }) withClearValue;
71
+
72
+ @Ref('optionsRef') optionsRef;
73
+ @Ref('inputRef') inputRef;
74
+
75
+ isCreatingOption = false;
76
+
77
+ get optionsFilteredBySearchValue() {
78
+ return this.options.filter((option) => option[this.itemText]
79
+ .toString()
80
+ .toLowerCase()
81
+ .includes(this.searchValue.toLowerCase()));
82
+ }
83
+
84
+ get filteredOptions() {
85
+ return this.multiple
86
+ ? this.optionsFilteredBySearchValue.filter(
87
+ (option) => !(this.value).includes(option[this.itemKey]),
88
+ )
89
+ : this.optionsFilteredBySearchValue.filter(
90
+ (option) => this.value !== option[this.itemKey],
91
+ );
92
+ }
93
+
94
+ get isOptionCreatable() {
95
+ return this.onCreate
96
+ && this.searchValue
97
+ && !this.options.map((option) => option[this.itemText]).includes(this.searchValue);
98
+ }
99
+
100
+ handleSearchValueChange(event) {
101
+ this.$emit('searchValueChange', (event.target)[this.itemKey]);
102
+ }
103
+
104
+ async getActiveOptionNode() {
105
+ await this.$nextTick();
106
+ return this.optionsRef
107
+ ? this.optionsRef.querySelector(`.${activeOptionClass}`)
108
+ : null;
109
+ }
110
+
111
+ selectOptionValue (optionValue) {
112
+ if (this.multiple) {
113
+ this.$emit('change', [
114
+ ...new Set([...(this.value), optionValue]),
115
+ ]);
116
+ } else {
117
+ this.deactivateDropdown();
118
+ this.$emit('change', optionValue);
119
+ }
120
+ }
121
+
122
+ createOption (newOptionLabel) {
123
+ this.isCreatingOption = true;
124
+ this.onCreate(
125
+ newOptionLabel,
126
+ (createdOptionValue) => {
127
+ this.isCreatingOption = false;
128
+ this.selectOptionValue(createdOptionValue);
129
+ },
130
+ );
131
+ };
132
+
133
+ async handleInputEnterKeyDown (event) {
134
+ event.preventDefault();
135
+ const $active = await this.getActiveOptionNode();
136
+ if (!$active) return;
137
+ const optionValueToSelect = $active.getAttribute(
138
+ 'data-select-option-value',
139
+ );
140
+ const optionLabelToCreate = $active.getAttribute(
141
+ 'data-create-option-label',
142
+ );
143
+ console.info($active, optionValueToSelect);
144
+ if (optionValueToSelect) {
145
+ this.selectOptionValue(optionValueToSelect);
146
+ } else if (optionLabelToCreate) {
147
+ this.createOption(optionLabelToCreate);
148
+ }
149
+ }
150
+
151
+ async handleInputArrowUpOrDownKeyDown (event) {
152
+ const $active = (await this.getActiveOptionNode());
153
+ const $options = this.optionsRef;
154
+ if (!$active || !$options) return;
155
+ const $optionsHeight = $options.getBoundingClientRect().height;
156
+ const $activeHeight = $active.getBoundingClientRect().height;
157
+ if (event.keyCode === 40) {
158
+ if ($options.lastElementChild === $active) {
159
+ $active.classList.remove(activeOptionClass);
160
+ $options.firstElementChild?.classList.add(activeOptionClass);
161
+ $options.scrollTop = 0;
162
+ } else {
163
+ $active.classList.remove(activeOptionClass);
164
+ $active.nextElementSibling?.classList.add(activeOptionClass);
165
+ if ($active.offsetTop > $options.scrollTop + $optionsHeight / 1.4) {
166
+ $options.scrollTop += $activeHeight;
167
+ }
168
+ }
169
+ } else if (event.keyCode === 38) {
170
+ if ($options.firstElementChild === $active) {
171
+ $active.classList.remove(activeOptionClass);
172
+ $options.lastElementChild?.classList.add(activeOptionClass);
173
+ $options.scrollTop = $options.scrollHeight;
174
+ } else {
175
+ $active.classList.remove(activeOptionClass);
176
+ $active.previousElementSibling?.classList.add(activeOptionClass);
177
+ if ($active.offsetTop < $options.scrollTop + $optionsHeight / 2.4) {
178
+ $options.scrollTop -= $activeHeight;
179
+ }
180
+ }
181
+ }
182
+ }
183
+
184
+ handleInputEscapeKeyDown(event) {
185
+ event.stopImmediatePropagation();
186
+ this.deactivateDropdown();
187
+ }
188
+
189
+ handleInputKeyDown(event) {
190
+ if (event.keyCode === 27) {
191
+ this.handleInputEscapeKeyDown(event);
192
+ } else if (event.keyCode === 13) {
193
+ this.handleInputEnterKeyDown(event);
194
+ } else if (event.keyCode === 40 || event.keyCode === 38) {
195
+ this.handleInputArrowUpOrDownKeyDown(event);
196
+ }
197
+ }
198
+
199
+ async handleOptionMouseEnter(event) {
200
+ const $active = await this.getActiveOptionNode();
201
+ if ($active) $active.classList.remove(activeOptionClass);
202
+ (event.currentTarget).classList.add(activeOptionClass);
203
+ }
204
+
205
+ async setFirstOptionAsActive() {
206
+ const $active = await this.getActiveOptionNode();
207
+ if (!this.optionsRef) return;
208
+ if ($active) $active.classList.remove(activeOptionClass);
209
+ if (this.optionsRef.firstElementChild) {
210
+ this.optionsRef.firstElementChild.classList.add(activeOptionClass);
211
+ }
212
+ }
213
+
214
+ mounted() {
215
+ this.setFirstOptionAsActive();
216
+ }
217
+ }
218
+ </script>
219
+
220
+ <style scoped lang="scss">
221
+ .dropdown {
222
+ z-index: 101;
223
+ position: absolute;
224
+ top: calc(100% + 4px);
225
+ left: -1px;
226
+ border-radius: 0 0 4px 4px;
227
+ background: #fff;
228
+ box-shadow: rgba(9, 30, 66, 0.25) 0px 4px 8px -2px,
229
+ rgba(9, 30, 66, 0.31) 0px 0px 1px;
230
+ width: 100%;
231
+ }
232
+
233
+ .dropdownInput {
234
+ padding: 10px 14px 8px;
235
+ width: 100%;
236
+ border: none;
237
+ color: #172b4d;
238
+ background: none;
239
+
240
+ &:focus {
241
+ outline: none;
242
+ }
243
+ }
244
+
245
+ .options {
246
+ padding: 5px 0;
247
+ max-height: 200px;
248
+ overflow-x: hidden;
249
+ overflow-y: auto;
250
+ -webkit-overflow-scrolling: touch;
251
+ // &::-webkit-scrollbar {
252
+ // width: 7px;
253
+ // }
254
+ // &::-webkit-scrollbar-track {
255
+ // background: none;
256
+ // }
257
+ // &::-webkit-scrollbar-thumb {
258
+ // // border-radius: 99px;
259
+ // background: #d1d1d1;
260
+ // }
261
+ }
262
+
263
+ .option {
264
+ padding: 8px 14px;
265
+ word-break: break-word;
266
+ cursor: pointer;
267
+
268
+ &.select-option-is-active {
269
+ background: #e8ecee;
270
+ }
271
+ }
272
+
273
+ .optionsNoResults {
274
+ padding: 0px 15px 10px;
275
+ color: #8993a4;
276
+ }
277
+ </style>
@@ -195,12 +195,14 @@
195
195
  }
196
196
  &__dropdown-menu {
197
197
  background-color: $body-bg;
198
- border: 2px solid $input-focus-border;
198
+ //border: 2px solid $input-focus-border;
199
+ border: 0 none;
199
200
  padding-left: 0.125rem !important;
200
201
  padding-right: 0.125rem !important;
201
202
  left: -2px;
202
203
  right: -2px;
203
204
  width: auto;
205
+ box-shadow: rgb(9 30 66 / 25%) 0px 4px 8px -2px, rgb(9 30 66 / 31%) 0px 0px 1px;
204
206
 
205
207
  @media (prefers-color-scheme: notdark) {
206
208
  background-color: $dark-body-bg;
@@ -1,5 +1,7 @@
1
1
  import { storiesOf } from '@storybook/vue';
2
2
  import itfSelect from './Select.vue';
3
+ import itfDropdown from './Dropdown';
4
+ import itfSelect2 from './AirSelect';
3
5
  import itfForm from '../form/Form.vue';
4
6
  import itfLabel from '../form/Label.vue';
5
7
  import itfButton from '../button/Button.vue';
@@ -15,6 +17,8 @@ storiesOf('Common', module)
15
17
  itfLabel,
16
18
  itfButton,
17
19
  itfSelect,
20
+ itfSelect2,
21
+ itfDropdown,
18
22
  itfDatePicker,
19
23
  itfDateRangePicker
20
24
  },
@@ -29,11 +33,16 @@ storiesOf('Common', module)
29
33
  customDays: {
30
34
  '2021-10-21': { text: '🎉', class: 'test' }
31
35
  },
36
+ value: [],
32
37
  countries: [
33
38
  {code: 'CA', country: 'Canada'},
34
39
  {code: 'US', country: 'United states'},
35
40
  {code: 'UA', country: 'Ukraine'},
36
41
  {code: 'TEST', country: 'ASdasd sdas dasd sad sadas da ad asdsad ad d ada dsadsadsadasd sadsa das dasd asdasd asdas da '}
42
+ ],
43
+ issuePriorityOptions: [
44
+ { label: 'High', value: 1 },
45
+ { label: 'Low', value: 2 }
37
46
  ]
38
47
  }
39
48
  },
@@ -65,6 +74,25 @@ storiesOf('Common', module)
65
74
 
66
75
  <itf-form ref="form">
67
76
 
77
+ <itf-select2
78
+ multiple
79
+ searchable
80
+ variant="normal"
81
+ :dropdownWidth="300"
82
+ v-model="value"
83
+ :options="issuePriorityOptions"
84
+ customRender
85
+ >
86
+ <template #default="{ label, icon, color }">
87
+ <itf-button secondary>
88
+ <div class="flex items-center">
89
+ <div class="pr-1 pl-2">
90
+ {{ label }}
91
+ </div>
92
+ </div>
93
+ </itf-button>
94
+ </template>
95
+ </itf-select2>
68
96
  <div class="row">
69
97
  <div class="col-6">
70
98
  <itf-label :rules="[$v.emptyValidation()]" :value="selected" label="From" required>
@@ -0,0 +1,36 @@
1
+ export const useOutsideClick = () => {
2
+ const root = typeof document !== 'undefined' ? document.body : null;
3
+ let handleClickOutside = null;
4
+ let handleKeydown = null;
5
+
6
+ return {
7
+ bind(element, onOutsideClick) {
8
+ handleClickOutside = (e) => {
9
+ if (element && !element.contains(e.target)) {
10
+ onOutsideClick();
11
+ }
12
+ };
13
+ handleKeydown = (e) => {
14
+ if (e.key === 'Escape') {
15
+ onOutsideClick();
16
+ }
17
+ };
18
+ if (root) {
19
+ root.addEventListener(
20
+ 'mousedown',
21
+ handleClickOutside,
22
+ );
23
+ root.addEventListener('keydown', handleKeydown);
24
+ }
25
+ },
26
+ unbind() {
27
+ if (root) {
28
+ root.removeEventListener(
29
+ 'mousedown',
30
+ handleClickOutside,
31
+ );
32
+ root.removeEventListener('keydown', handleKeydown);
33
+ }
34
+ }
35
+ };
36
+ };
@@ -1,5 +1,5 @@
1
1
  <template>
2
- <div v-show="tabsManager && tabsManager.getValue() === id" :class="{ filled }" class="itf-tab-content" :data-test="`itf-tab-content-${id || _uid}`">
2
+ <div v-if="tabsManager && tabsManager.getValue() === id" :class="{ filled }" class="itf-tab-content" :data-test="`itf-tab-content-${id || _uid}`">
3
3
  <slot />
4
4
  </div>
5
5
  </template>