@itfin/components 1.2.107 → 1.2.109
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/copyToClipboard/CopyToClipboard.vue +67 -0
- package/src/components/customize/Inline/Multiselect.vue +21 -3
- package/src/components/customize/Inline/Select.vue +4 -1
- package/src/components/customize/Inline/Text.vue +6 -1
- package/src/components/customize/PropertiesEditMenu.vue +3 -4
- package/src/components/customize/PropertiesList.vue +6 -2
- package/src/components/kanban/BoardCard.vue +1 -1
- package/src/components/kanban/BoardColumn.vue +1 -1
- package/src/components/kanban/styles.scss +11 -12
- package/src/components/select/Select.vue +1 -2
- package/src/locales/en.js +3 -0
- package/src/locales/uk.js +3 -0
package/package.json
CHANGED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<itf-button icon small @click.stop="copy">
|
|
3
|
+
<slot>
|
|
4
|
+
<itf-icon name="clipboard_copy" />
|
|
5
|
+
</slot>
|
|
6
|
+
</itf-button>
|
|
7
|
+
</template>
|
|
8
|
+
<script>
|
|
9
|
+
import { Vue, Component, Prop } from 'vue-property-decorator';
|
|
10
|
+
import itfIcon from '../icon/Icon';
|
|
11
|
+
import itfButton from '../button/Button';
|
|
12
|
+
|
|
13
|
+
export default @Component({
|
|
14
|
+
components: {
|
|
15
|
+
itfIcon,
|
|
16
|
+
itfButton
|
|
17
|
+
}
|
|
18
|
+
})
|
|
19
|
+
class CopyToClipboard extends Vue {
|
|
20
|
+
@Prop(String) value;
|
|
21
|
+
|
|
22
|
+
async copy () {
|
|
23
|
+
const showSuccess = this.$showSuccess;
|
|
24
|
+
const showError = this.$showError;
|
|
25
|
+
const $t = this.$t.bind(this);
|
|
26
|
+
|
|
27
|
+
function fallbackCopyTextToClipboard (text) {
|
|
28
|
+
const textArea = document.createElement('textarea');
|
|
29
|
+
textArea.value = text;
|
|
30
|
+
|
|
31
|
+
// Avoid scrolling to bottom
|
|
32
|
+
textArea.style.top = '0';
|
|
33
|
+
textArea.style.left = '0';
|
|
34
|
+
textArea.style.position = 'fixed';
|
|
35
|
+
|
|
36
|
+
document.body.appendChild(textArea);
|
|
37
|
+
textArea.focus();
|
|
38
|
+
textArea.select();
|
|
39
|
+
|
|
40
|
+
try {
|
|
41
|
+
if (document.execCommand('copy')) {
|
|
42
|
+
showSuccess($t('components.copyToClipboard.copyingToClipboardWasSuccessful'));
|
|
43
|
+
}
|
|
44
|
+
} catch (err) {
|
|
45
|
+
showError(err.message);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
document.body.removeChild(textArea);
|
|
49
|
+
}
|
|
50
|
+
async function copyTextToClipboard (text) {
|
|
51
|
+
if (!navigator.clipboard) {
|
|
52
|
+
fallbackCopyTextToClipboard(text);
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
try {
|
|
56
|
+
await navigator.clipboard.writeText(text);
|
|
57
|
+
|
|
58
|
+
showSuccess($t('components.copyToClipboard.copyingToClipboardWasSuccessful'));
|
|
59
|
+
} catch (err) {
|
|
60
|
+
showError(err.message);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
await copyTextToClipboard(this.value);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
</script>
|
|
@@ -2,17 +2,20 @@
|
|
|
2
2
|
<div class="itf-inline-edit">
|
|
3
3
|
<itf-select
|
|
4
4
|
ref="input"
|
|
5
|
-
class="input"
|
|
5
|
+
class="input flex-grow-1"
|
|
6
6
|
role="button"
|
|
7
7
|
multiple
|
|
8
8
|
:placeholder="focused ? 'Search for an option...' : 'Empty'"
|
|
9
9
|
:tabindex="1"
|
|
10
10
|
:disabled="!editable"
|
|
11
|
-
:options="
|
|
11
|
+
:options="options"
|
|
12
12
|
:reduce="option => option.Name"
|
|
13
13
|
:get-option-label="option => option.Name"
|
|
14
14
|
@search:focus="$emit('focus')"
|
|
15
|
-
@search:blur="$emit('blur')"
|
|
15
|
+
@search:blur="$emit('blur')"
|
|
16
|
+
:value="itemsValue"
|
|
17
|
+
@input="onInput"
|
|
18
|
+
>
|
|
16
19
|
<template #option="{ option }">
|
|
17
20
|
<div>{{ option.Name }}</div>
|
|
18
21
|
</template>
|
|
@@ -51,5 +54,20 @@ class itfCustomizeInlineMultiselect extends Vue {
|
|
|
51
54
|
@Prop(Boolean) focused;
|
|
52
55
|
@Prop(Boolean) editable;
|
|
53
56
|
@Prop() field;
|
|
57
|
+
|
|
58
|
+
get itemsValue () {
|
|
59
|
+
return (this.value || '').toString().split(';;;').filter(v => v.length);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
get options() {
|
|
63
|
+
const list = (this.field && this.field.Options) || [];
|
|
64
|
+
const notExists = this.itemsValue.filter(v => !list.find(o => o.Name === v));
|
|
65
|
+
|
|
66
|
+
return [...list, ...notExists.map(v => ({ Name: v }))];
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
onInput(val) {
|
|
70
|
+
this.$emit('input', val.join(';;;'));
|
|
71
|
+
}
|
|
54
72
|
}
|
|
55
73
|
</script>
|
|
@@ -10,7 +10,10 @@
|
|
|
10
10
|
:reduce="option => option.Name"
|
|
11
11
|
:get-option-label="option => option.Name"
|
|
12
12
|
@search:focus="$emit('focus')"
|
|
13
|
-
@search:blur="$emit('blur')"
|
|
13
|
+
@search:blur="$emit('blur')"
|
|
14
|
+
:value="value"
|
|
15
|
+
@input="$emit('input', $event)"
|
|
16
|
+
>
|
|
14
17
|
<template #option="{ option }">
|
|
15
18
|
<div>{{ option.Name }}</div>
|
|
16
19
|
</template>
|
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
:contenteditable="editable"
|
|
15
15
|
v-html="value">
|
|
16
16
|
</div>
|
|
17
|
+
<itf-copy-to-clipboard :value="value" />
|
|
17
18
|
</div>
|
|
18
19
|
</template>
|
|
19
20
|
<style lang="scss" scoped>
|
|
@@ -36,12 +37,16 @@
|
|
|
36
37
|
</style>
|
|
37
38
|
<script>
|
|
38
39
|
import { Vue, Component, Model, Prop } from 'vue-property-decorator';
|
|
40
|
+
import itfIcon from '../../icon/Icon.vue';
|
|
39
41
|
import itfTextField from '../../text-field/TextField.vue';
|
|
42
|
+
import itfCopyToClipboard from '../../copyToClipboard/CopyToClipboard';
|
|
40
43
|
|
|
41
44
|
export default @Component({
|
|
42
45
|
name: 'itfCustomizeInlineText',
|
|
43
46
|
components: {
|
|
44
|
-
|
|
47
|
+
itfIcon,
|
|
48
|
+
itfTextField,
|
|
49
|
+
itfCopyToClipboard
|
|
45
50
|
}
|
|
46
51
|
})
|
|
47
52
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<div class="itf-append-context" style="min-width: 300px">
|
|
3
|
-
<div class="d-flex align-items-center px-3 py-2">
|
|
3
|
+
<div class="d-flex align-items-center px-3 py-2" sortable-skip>
|
|
4
4
|
<!--div class="me-1">
|
|
5
5
|
<itf-icon-popover title="Icon" removable :value="value.Icon" @input="setParam({ Icon: $event })">
|
|
6
6
|
<itf-button slot="activator" icon>
|
|
@@ -8,7 +8,6 @@
|
|
|
8
8
|
</itf-button>
|
|
9
9
|
</itf-icon-popover>
|
|
10
10
|
</div-->
|
|
11
|
-
|
|
12
11
|
<div class="flex-grow-1">
|
|
13
12
|
<itf-text-field ref="input" :value="value.Name" @input="setParam({ Name: $event })" />
|
|
14
13
|
</div>
|
|
@@ -39,7 +38,7 @@
|
|
|
39
38
|
</div>
|
|
40
39
|
</div>
|
|
41
40
|
|
|
42
|
-
<div v-if="isNewOptionVisible" class="px-3">
|
|
41
|
+
<div v-if="isNewOptionVisible" class="px-3" sortable-skip>
|
|
43
42
|
<itf-text-field
|
|
44
43
|
ref="newOptionInput"
|
|
45
44
|
v-model="newOption"
|
|
@@ -117,7 +116,7 @@ import itfIconPopover from '../popover/IconPopover.vue';
|
|
|
117
116
|
import { INLINE_TYPES } from './constants';
|
|
118
117
|
import itfDeleteConfirmModal from '../modal/DeleteConfirmModal.vue';
|
|
119
118
|
import PropertyItem from './PropertyItem.vue';
|
|
120
|
-
import PropertiesPopupMenu from
|
|
119
|
+
import PropertiesPopupMenu from './PropertiesPopupMenu.vue';
|
|
121
120
|
|
|
122
121
|
export default @Component({
|
|
123
122
|
components: {
|
|
@@ -165,7 +165,9 @@ class itfPropertiesList extends Vue {
|
|
|
165
165
|
prop.hideEditMenu();
|
|
166
166
|
}
|
|
167
167
|
setTimeout(() => {
|
|
168
|
-
this.$refs.properties[list.length - 1]
|
|
168
|
+
if (this.$refs.properties && this.$refs.properties[list.length - 1]) {
|
|
169
|
+
this.$refs.properties[list.length - 1].showEditMenu();
|
|
170
|
+
}
|
|
169
171
|
}, 10);
|
|
170
172
|
});
|
|
171
173
|
}
|
|
@@ -186,7 +188,9 @@ class itfPropertiesList extends Vue {
|
|
|
186
188
|
|
|
187
189
|
this.$nextTick(() => {
|
|
188
190
|
setTimeout(() => {
|
|
189
|
-
this.$refs.properties[list.length - 1]
|
|
191
|
+
if (this.$refs.properties && this.$refs.properties[list.length - 1]) {
|
|
192
|
+
this.$refs.properties[list.length - 1].showEditMenu();
|
|
193
|
+
}
|
|
190
194
|
}, 10);
|
|
191
195
|
});
|
|
192
196
|
}
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
{{ newItemText }}
|
|
16
16
|
</itf-button>
|
|
17
17
|
|
|
18
|
-
<div class="itf-board-cards-wrapper" ref="container">
|
|
18
|
+
<div class="itf-board-cards-wrapper" ref="container" :class="{'empty-column': !items.length}">
|
|
19
19
|
<template v-if="!items.length">
|
|
20
20
|
<slot name="empty"></slot>
|
|
21
21
|
</template>
|
|
@@ -22,6 +22,8 @@
|
|
|
22
22
|
display: flex;
|
|
23
23
|
flex-wrap: nowrap;
|
|
24
24
|
flex-direction: column;
|
|
25
|
+
overflow-x: scroll;
|
|
26
|
+
overflow-y: hidden;
|
|
25
27
|
|
|
26
28
|
.draggable-mirror {
|
|
27
29
|
z-index: 100000001;
|
|
@@ -107,6 +109,7 @@
|
|
|
107
109
|
.itf-board-column {
|
|
108
110
|
position: relative;
|
|
109
111
|
width: var(--itf-board-column-width);
|
|
112
|
+
min-width: var(--itf-board-column-width);
|
|
110
113
|
border-radius: 0 0 var(--bs-border-radius) var(--bs-border-radius);
|
|
111
114
|
|
|
112
115
|
&__title {
|
|
@@ -150,12 +153,18 @@
|
|
|
150
153
|
}
|
|
151
154
|
|
|
152
155
|
.itf-board-titles {
|
|
156
|
+
width: max-content;
|
|
153
157
|
border-bottom: 1px solid var(--itf-board-card-border-color);
|
|
154
158
|
}
|
|
155
159
|
|
|
156
160
|
.itf-board-columns {
|
|
157
161
|
max-height: 100%;
|
|
162
|
+
padding-bottom: 45px;
|
|
158
163
|
|
|
164
|
+
.empty-column .itf-board-card-space.bottom {
|
|
165
|
+
top: 0;
|
|
166
|
+
height: 100%;
|
|
167
|
+
}
|
|
159
168
|
.itf-board-column {
|
|
160
169
|
display: flex;
|
|
161
170
|
}
|
|
@@ -181,8 +190,8 @@
|
|
|
181
190
|
right: 0;
|
|
182
191
|
top: -50px;
|
|
183
192
|
left: 0;
|
|
184
|
-
bottom:
|
|
185
|
-
height:
|
|
193
|
+
bottom: 0px;
|
|
194
|
+
height: 50px;
|
|
186
195
|
}
|
|
187
196
|
&.bottom {
|
|
188
197
|
top: auto;
|
|
@@ -225,17 +234,7 @@
|
|
|
225
234
|
font-size: .875rem;
|
|
226
235
|
word-wrap: break-word;
|
|
227
236
|
-webkit-line-clamp: unset;
|
|
228
|
-
overflow: hidden;
|
|
229
237
|
word-break: break-word;
|
|
230
|
-
|
|
231
|
-
.itf-board-card-inner-content {
|
|
232
|
-
padding-right: 0.2rem;
|
|
233
|
-
overflow-x: hidden;
|
|
234
|
-
}
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
.itf-board-card-footer {
|
|
238
|
-
overflow-x: hidden;
|
|
239
238
|
}
|
|
240
239
|
}
|
|
241
240
|
|
|
@@ -32,9 +32,8 @@
|
|
|
32
32
|
<itf-button
|
|
33
33
|
icon
|
|
34
34
|
small
|
|
35
|
-
v-if="multiple"
|
|
35
|
+
v-if="multiple && !disabled"
|
|
36
36
|
ref="deselectButtons"
|
|
37
|
-
:disabled="disabled"
|
|
38
37
|
class="vs__deselect"
|
|
39
38
|
:title="`Deselect ${getOptionLabel(option)}`"
|
|
40
39
|
:aria-label="`Deselect ${getOptionLabel(option)}`"
|
package/src/locales/en.js
CHANGED
|
@@ -93,5 +93,8 @@ module.exports = {
|
|
|
93
93
|
hideMoreProperties: 'Hide {n} property|Hide {n} properties',
|
|
94
94
|
deleteItem: 'Delete',
|
|
95
95
|
areYouSureYouWantToDeleteThisItem: 'Are you sure you want to remove this option?',
|
|
96
|
+
},
|
|
97
|
+
copyToClipboard: {
|
|
98
|
+
copyingToClipboardWasSuccessful: 'Copying to clipboard was successful',
|
|
96
99
|
}
|
|
97
100
|
};
|
package/src/locales/uk.js
CHANGED
|
@@ -93,5 +93,8 @@ module.exports = {
|
|
|
93
93
|
hideMoreProperties: 'Приховати {n} поле|Приховати {n} полів',
|
|
94
94
|
deleteItem: 'Видалити',
|
|
95
95
|
areYouSureYouWantToDeleteThisItem: 'Ви впевнені, що хочете видалити цю опцію?',
|
|
96
|
+
},
|
|
97
|
+
copyToClipboard: {
|
|
98
|
+
copyingToClipboardWasSuccessful: 'Скопійовано в буфер',
|
|
96
99
|
}
|
|
97
100
|
};
|