@itfin/components 1.0.77 → 1.0.81
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 +1 -1
- package/src/components/modal/Modal.vue +11 -5
- package/src/components/segmented-control/SegmentedControl.vue +12 -5
- package/src/components/select/AirSelect.vue +30 -25
- package/src/components/select/Dropdown.vue +12 -10
- package/src/components/select/useOutsideClick.js +15 -11
package/package.json
CHANGED
|
@@ -7,7 +7,9 @@
|
|
|
7
7
|
>
|
|
8
8
|
<div class="modal-content itf-append-context" ref="content">
|
|
9
9
|
<div class="modal-header">
|
|
10
|
-
<
|
|
10
|
+
<slot name="title">
|
|
11
|
+
<h5 class="modal-title" :id="modalId">{{title}}</h5>
|
|
12
|
+
</slot>
|
|
11
13
|
<itf-button icon data-bs-dismiss="modal" aria-label="Close" class="btn-close"></itf-button>
|
|
12
14
|
</div>
|
|
13
15
|
<div class="modal-body" v-if="value">
|
|
@@ -96,7 +98,7 @@ class itfModal extends Vue {
|
|
|
96
98
|
setTimeout(() => {
|
|
97
99
|
this.modalEl.show();
|
|
98
100
|
modalStack.push(this);
|
|
99
|
-
}, 500);
|
|
101
|
+
}, modalStack.length ? 500 : 0);
|
|
100
102
|
} else {
|
|
101
103
|
this.modalEl.hide();
|
|
102
104
|
modalStack.pop();
|
|
@@ -104,7 +106,7 @@ class itfModal extends Vue {
|
|
|
104
106
|
setTimeout(() => {
|
|
105
107
|
modalStack[modalStack.length - 1].show();
|
|
106
108
|
modalStack[modalStack.length - 1].bindEvents();
|
|
107
|
-
}, 500);
|
|
109
|
+
}, modalStack.length ? 500 : 0);
|
|
108
110
|
}
|
|
109
111
|
}
|
|
110
112
|
}
|
|
@@ -131,12 +133,16 @@ class itfModal extends Vue {
|
|
|
131
133
|
|
|
132
134
|
bindEvents() {
|
|
133
135
|
this.preventEvents = false;
|
|
134
|
-
this.$refs.modal
|
|
136
|
+
if (this.$refs.modal) {
|
|
137
|
+
this.$refs.modal.addEventListener('hidden.bs.modal', this.onClose);
|
|
138
|
+
}
|
|
135
139
|
}
|
|
136
140
|
|
|
137
141
|
unbindEvents() {
|
|
138
142
|
this.preventEvents = true;
|
|
139
|
-
this.$refs.modal
|
|
143
|
+
if (this.$refs.modal) {
|
|
144
|
+
this.$refs.modal.removeEventListener('hidden.bs.modal', this.onClose);
|
|
145
|
+
}
|
|
140
146
|
}
|
|
141
147
|
|
|
142
148
|
show() {
|
|
@@ -165,6 +165,8 @@ class itfSegmentedControl extends Vue {
|
|
|
165
165
|
@Prop({ type: Boolean, default: false }) returnObject;
|
|
166
166
|
@Prop({ type: Boolean, default: false }) disabled;
|
|
167
167
|
|
|
168
|
+
timers = [];
|
|
169
|
+
|
|
168
170
|
get name () {
|
|
169
171
|
return `sc${this._uid}`;
|
|
170
172
|
}
|
|
@@ -210,20 +212,25 @@ class itfSegmentedControl extends Vue {
|
|
|
210
212
|
updatePillPosition(this);
|
|
211
213
|
|
|
212
214
|
function updatePillPosition (component) {
|
|
215
|
+
component.timers.forEach((timer) => {
|
|
216
|
+
clearTimeout(timer);
|
|
217
|
+
});
|
|
218
|
+
component.timers = [];
|
|
213
219
|
component.$refs.input.forEach((elem, index) => {
|
|
214
220
|
if (elem.checked) {
|
|
215
221
|
component.$nextTick(() => moveBackgroundPillToElement(component, elem, index));
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
222
|
+
component.timers = [
|
|
223
|
+
setTimeout(() => moveBackgroundPillToElement(component, elem, index), 500),
|
|
224
|
+
setTimeout(() => moveBackgroundPillToElement(component, elem, index), 750),
|
|
225
|
+
setTimeout(() => moveBackgroundPillToElement(component, elem, index), 1500),
|
|
226
|
+
setTimeout(() => moveBackgroundPillToElement(component, elem, index), 3000)
|
|
227
|
+
];
|
|
220
228
|
}
|
|
221
229
|
})
|
|
222
230
|
}
|
|
223
231
|
|
|
224
232
|
function moveBackgroundPillToElement (component, elem, index) {
|
|
225
233
|
const slider = component.$refs.slider;
|
|
226
|
-
console.info('init 2', slider);
|
|
227
234
|
if (slider) {
|
|
228
235
|
slider.style.transform = 'translateX(' + (elem.offsetWidth * index) + 'px)';
|
|
229
236
|
}
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
|
|
15
15
|
<slot
|
|
16
16
|
v-if="!isValueEmpty && !multiple && customRender"
|
|
17
|
-
|
|
17
|
+
:option="getOption(localValue)"
|
|
18
18
|
/>
|
|
19
19
|
<template v-else>
|
|
20
20
|
{{ getOptionLabel(localValue) }}
|
|
@@ -28,13 +28,11 @@
|
|
|
28
28
|
>
|
|
29
29
|
<slot
|
|
30
30
|
v-if="customRender"
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
remove: removeOptionValue
|
|
35
|
-
}"
|
|
31
|
+
:option="getOption(optionValue)"
|
|
32
|
+
:value="optionValue"
|
|
33
|
+
:remove="removeOptionValue"
|
|
36
34
|
/>
|
|
37
|
-
<div v-else class="valueMultiItem text-textDarkest">
|
|
35
|
+
<div v-else class="valueMultiItem text-textDarkest" :style="getOptionColor(optionValue) ? `background-color: #${getOptionColor(optionValue)}; color: #fff` : ''">
|
|
38
36
|
<div class="valueMultiItemLabel">
|
|
39
37
|
{{ getOptionLabel(optionValue) }}
|
|
40
38
|
</div>
|
|
@@ -68,11 +66,11 @@
|
|
|
68
66
|
</div>
|
|
69
67
|
</div>
|
|
70
68
|
|
|
71
|
-
<
|
|
69
|
+
<icon
|
|
72
70
|
v-if="(!multiple || isValueEmpty) && variant !== 'empty'"
|
|
73
71
|
class="ml-auto mr-1"
|
|
74
|
-
name="
|
|
75
|
-
></
|
|
72
|
+
name="chevron_down"
|
|
73
|
+
></icon>
|
|
76
74
|
</div>
|
|
77
75
|
|
|
78
76
|
<Dropdown
|
|
@@ -86,13 +84,15 @@
|
|
|
86
84
|
:deactivateDropdown="deactivateDropdown"
|
|
87
85
|
:options="options"
|
|
88
86
|
:multiple="multiple"
|
|
87
|
+
:item-key="itemKey"
|
|
88
|
+
:item-text="itemText"
|
|
89
89
|
:withClearValue="withClearValue"
|
|
90
90
|
@change="handleChange"
|
|
91
91
|
@searchValueChange="handleSearchValueChange"
|
|
92
92
|
>
|
|
93
|
-
<template
|
|
94
|
-
<slot v-if="customRenderOption" name="option"
|
|
95
|
-
<slot v-else
|
|
93
|
+
<template #option="{ option }">
|
|
94
|
+
<slot v-if="customRenderOption" name="option" :option="option" />
|
|
95
|
+
<slot v-else :option="option" />
|
|
96
96
|
</template>
|
|
97
97
|
</Dropdown>
|
|
98
98
|
</div>
|
|
@@ -101,6 +101,7 @@
|
|
|
101
101
|
<script>
|
|
102
102
|
import { Component, Model, Prop, Ref } from 'vue-property-decorator';
|
|
103
103
|
import Vue from 'vue';
|
|
104
|
+
import Icon from '../icon/Icon.vue';
|
|
104
105
|
import Dropdown from './Dropdown.vue';
|
|
105
106
|
import { useOutsideClick } from './useOutsideClick';
|
|
106
107
|
|
|
@@ -108,7 +109,7 @@ const { bind, unbind } = useOutsideClick();
|
|
|
108
109
|
|
|
109
110
|
export default @Component({
|
|
110
111
|
name: 'itfAirSelect',
|
|
111
|
-
components: { Dropdown },
|
|
112
|
+
components: { Dropdown, Icon },
|
|
112
113
|
})
|
|
113
114
|
class itfAirSelect extends Vue {
|
|
114
115
|
@Model('input', { type: [Array, String, Number], default: undefined }) value;
|
|
@@ -117,6 +118,10 @@ class itfAirSelect extends Vue {
|
|
|
117
118
|
|
|
118
119
|
@Prop({ type: String, default: 'normal' }) variant;
|
|
119
120
|
|
|
121
|
+
@Prop({ type: String, default: 'value' }) itemKey;
|
|
122
|
+
@Prop({ type: String, default: 'label' }) itemText;
|
|
123
|
+
@Prop({ type: String, default: 'color' }) itemColor;
|
|
124
|
+
|
|
120
125
|
@Prop({ type: String, default: undefined }) name;
|
|
121
126
|
|
|
122
127
|
@Prop({ type: Boolean, default: false }) searchable;
|
|
@@ -133,6 +138,9 @@ class itfAirSelect extends Vue {
|
|
|
133
138
|
|
|
134
139
|
@Prop({ type: Boolean, default: false }) withClearValue;
|
|
135
140
|
|
|
141
|
+
@Prop({ type: Boolean, default: false }) customRender;
|
|
142
|
+
@Prop({ type: Boolean, default: false }) customRenderOption;
|
|
143
|
+
|
|
136
144
|
@Ref('selectRef') selectRef;
|
|
137
145
|
|
|
138
146
|
@Ref('dropdownRef') dropdownRef;
|
|
@@ -141,14 +149,6 @@ class itfAirSelect extends Vue {
|
|
|
141
149
|
|
|
142
150
|
searchValue = '';
|
|
143
151
|
|
|
144
|
-
get customRender() {
|
|
145
|
-
return !!this.$slots.default;
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
get customRenderOption() {
|
|
149
|
-
return !!this.$slots.option;
|
|
150
|
-
}
|
|
151
|
-
|
|
152
152
|
data() {
|
|
153
153
|
return {
|
|
154
154
|
stateValue: this.defaultValue || (this.multiple ? [] : null),
|
|
@@ -156,11 +156,15 @@ class itfAirSelect extends Vue {
|
|
|
156
156
|
}
|
|
157
157
|
|
|
158
158
|
getOption(optionValue) {
|
|
159
|
-
return this.options.find((option) => option.
|
|
159
|
+
return this.options.find((option) => option[this.itemKey] === optionValue);
|
|
160
160
|
}
|
|
161
161
|
|
|
162
162
|
getOptionLabel(optionValue) {
|
|
163
|
-
return (this.getOption(optionValue) || {
|
|
163
|
+
return (this.getOption(optionValue) || { [this.itemText]: '' })[this.itemText];
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
getOptionColor(optionValue) {
|
|
167
|
+
return (this.getOption(optionValue) || { [this.itemColor]: '' })[this.itemColor];
|
|
164
168
|
}
|
|
165
169
|
|
|
166
170
|
get isControlled() {
|
|
@@ -173,7 +177,7 @@ class itfAirSelect extends Vue {
|
|
|
173
177
|
|
|
174
178
|
preserveValueType(newValue) {
|
|
175
179
|
const areOptionValuesNumbers = this.options.some(
|
|
176
|
-
(option) => typeof option.
|
|
180
|
+
(option) => typeof option[this.itemKey] === 'number',
|
|
177
181
|
);
|
|
178
182
|
if (areOptionValuesNumbers) {
|
|
179
183
|
if (this.multiple) {
|
|
@@ -256,6 +260,7 @@ class itfAirSelect extends Vue {
|
|
|
256
260
|
align-items: center;
|
|
257
261
|
width: 100%;
|
|
258
262
|
min-height: 32px;
|
|
263
|
+
justify-content: space-between;
|
|
259
264
|
}
|
|
260
265
|
.placeholder {
|
|
261
266
|
color: #8993a4;
|
|
@@ -14,13 +14,13 @@
|
|
|
14
14
|
<div
|
|
15
15
|
class="option text-textDarkest"
|
|
16
16
|
v-for="option in filteredOptions"
|
|
17
|
-
:key="option
|
|
18
|
-
:data-select-option-value="option
|
|
19
|
-
:data-testid="`select-option:${option
|
|
17
|
+
:key="option[itemKey]"
|
|
18
|
+
:data-select-option-value="option[itemKey]"
|
|
19
|
+
:data-testid="`select-option:${option[itemText]}`"
|
|
20
20
|
@mouseenter="handleOptionMouseEnter"
|
|
21
|
-
@click="selectOptionValue(option
|
|
21
|
+
@click="selectOptionValue(option[itemKey])"
|
|
22
22
|
>
|
|
23
|
-
<slot name="option"
|
|
23
|
+
<slot name="option" :option="option">{{ option[itemText] }}</slot>
|
|
24
24
|
</div>
|
|
25
25
|
|
|
26
26
|
<div
|
|
@@ -60,6 +60,8 @@ class itfSegmentedControl extends Vue {
|
|
|
60
60
|
@Prop({ type: [Array, String, Number], default: undefined }) value;
|
|
61
61
|
@Prop({ type: Boolean }) isValueEmpty;
|
|
62
62
|
@Prop({ type: Boolean }) searchable;
|
|
63
|
+
@Prop({ type: String, default: 'value' }) itemKey;
|
|
64
|
+
@Prop({ type: String, default: 'label' }) itemText;
|
|
63
65
|
@Prop({ type: String, default: '' }) searchValue;
|
|
64
66
|
@Prop({ type: Function, required: true }) deactivateDropdown;
|
|
65
67
|
@Prop({ type: Array, required: true }) options;
|
|
@@ -73,7 +75,7 @@ class itfSegmentedControl extends Vue {
|
|
|
73
75
|
isCreatingOption = false;
|
|
74
76
|
|
|
75
77
|
get optionsFilteredBySearchValue() {
|
|
76
|
-
return this.options.filter((option) => option.
|
|
78
|
+
return this.options.filter((option) => option[this.itemText]
|
|
77
79
|
.toString()
|
|
78
80
|
.toLowerCase()
|
|
79
81
|
.includes(this.searchValue.toLowerCase()));
|
|
@@ -82,21 +84,21 @@ class itfSegmentedControl extends Vue {
|
|
|
82
84
|
get filteredOptions() {
|
|
83
85
|
return this.multiple
|
|
84
86
|
? this.optionsFilteredBySearchValue.filter(
|
|
85
|
-
(option) => !(this.value).includes(option.
|
|
87
|
+
(option) => !(this.value).includes(option[this.itemKey]),
|
|
86
88
|
)
|
|
87
89
|
: this.optionsFilteredBySearchValue.filter(
|
|
88
|
-
(option) => this.value !== option.
|
|
90
|
+
(option) => this.value !== option[this.itemKey],
|
|
89
91
|
);
|
|
90
92
|
}
|
|
91
93
|
|
|
92
94
|
get isOptionCreatable() {
|
|
93
95
|
return this.onCreate
|
|
94
96
|
&& this.searchValue
|
|
95
|
-
&& !this.options.map((option) => option.
|
|
97
|
+
&& !this.options.map((option) => option[this.itemText]).includes(this.searchValue);
|
|
96
98
|
}
|
|
97
99
|
|
|
98
100
|
handleSearchValueChange(event) {
|
|
99
|
-
this.$emit('searchValueChange', (event.target).
|
|
101
|
+
this.$emit('searchValueChange', (event.target)[this.itemKey]);
|
|
100
102
|
}
|
|
101
103
|
|
|
102
104
|
async getActiveOptionNode() {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export const useOutsideClick = () => {
|
|
2
|
-
const root = document.body;
|
|
2
|
+
const root = typeof document !== 'undefined' ? document.body : null;
|
|
3
3
|
let handleClickOutside = null;
|
|
4
4
|
let handleKeydown = null;
|
|
5
5
|
|
|
@@ -15,18 +15,22 @@ export const useOutsideClick = () => {
|
|
|
15
15
|
onOutsideClick();
|
|
16
16
|
}
|
|
17
17
|
};
|
|
18
|
-
root
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
18
|
+
if (root) {
|
|
19
|
+
root.addEventListener(
|
|
20
|
+
'mousedown',
|
|
21
|
+
handleClickOutside,
|
|
22
|
+
);
|
|
23
|
+
root.addEventListener('keydown', handleKeydown);
|
|
24
|
+
}
|
|
23
25
|
},
|
|
24
26
|
unbind() {
|
|
25
|
-
root
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
27
|
+
if (root) {
|
|
28
|
+
root.removeEventListener(
|
|
29
|
+
'mousedown',
|
|
30
|
+
handleClickOutside,
|
|
31
|
+
);
|
|
32
|
+
root.removeEventListener('keydown', handleKeydown);
|
|
33
|
+
}
|
|
30
34
|
}
|
|
31
35
|
};
|
|
32
36
|
};
|