@itfin/components 1.2.87 → 1.2.89
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 +2 -1
- package/src/assets/scss/boostrap.scss +124 -0
- package/src/assets/scss/components/_icon.scss +3 -1
- package/src/assets/scss/main.scss +11 -0
- package/src/components/customize/Inline/Date.vue +84 -0
- package/src/components/customize/Inline/Multiselect.vue +55 -0
- package/src/components/customize/Inline/Text.vue +89 -0
- package/src/components/customize/PropertiesEditMenu.vue +110 -8
- package/src/components/customize/PropertiesList.vue +92 -102
- package/src/components/customize/PropertiesPopupMenu.vue +5 -4
- package/src/components/customize/PropertyInlineEdit.vue +26 -28
- package/src/components/customize/PropertyItem.vue +192 -0
- package/src/components/customize/constants.js +9 -0
- package/src/components/customize/index.stories.js +5 -3
- package/src/components/dropdown/Dropdown.vue +4 -1
- package/src/components/icon/MdiIcon.vue +70 -0
- package/src/components/modal/DeleteConfirmModal.vue +3 -1
- package/src/components/popover/IconPopover.vue +16 -10
- package/src/components/select/Select.vue +2 -2
- package/src/components/text-field/MoneyField.vue +9 -2
- package/src/helpers/formatters.js +3 -0
- package/src/locales/en.js +8 -0
|
@@ -4,54 +4,46 @@
|
|
|
4
4
|
require-sortable-attribute
|
|
5
5
|
v-model="list"
|
|
6
6
|
axis="y"
|
|
7
|
+
:read-only="!editable"
|
|
7
8
|
auto-scroll="window"
|
|
8
9
|
>
|
|
9
|
-
<
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
10
|
+
<property-item
|
|
11
|
+
v-for="(field, n) of list"
|
|
12
|
+
:key="n"
|
|
13
|
+
ref="properties"
|
|
14
|
+
:field="field"
|
|
15
|
+
:editable="editable"
|
|
16
|
+
:lock-fields="lockFields"
|
|
17
|
+
:value="value[field.Id]"
|
|
18
|
+
@input="$emit('input', { ...value, [field.Id]: $event })"
|
|
19
|
+
@delete="onDelete(field)"
|
|
20
|
+
@duplicate="onDuplicate(field)"
|
|
21
|
+
@update:field="onChange($event, n)"
|
|
22
|
+
/>
|
|
23
|
+
</sortable>
|
|
21
24
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
@edit="isEditMode = true"
|
|
38
|
-
/>
|
|
39
|
-
</itf-dropdown>
|
|
40
|
-
</div>
|
|
41
|
-
<div class="b-properties-list__value" sortable-skip>
|
|
42
|
-
<div role="button" tabindex="1">
|
|
43
|
-
<property-inline-edit :value="value[field.Id]" @input="onValueSet(field, $event)" />
|
|
44
|
-
<span v-if="value && value[field.Id]">{{ value[field.Id] }}</span>
|
|
45
|
-
<span v-else class="text-muted"> ({{ $t('notSet') }})</span>
|
|
25
|
+
<div v-if="editable && !lockFields" class="text-start">
|
|
26
|
+
<div v-if="isShowAll">
|
|
27
|
+
<itf-dropdown ref="newItemDd" autoclose="outside" class="flex-grow-1 mw-100" shadow :button-options="{ small: true }">
|
|
28
|
+
<template #button>
|
|
29
|
+
<span class="text-muted d-flex align-items-center">
|
|
30
|
+
<itf-icon name="plus" />
|
|
31
|
+
Add property
|
|
32
|
+
</span>
|
|
33
|
+
</template>
|
|
34
|
+
<div><h6 class="dropdown-header">{{ $t('components.customize.type') }}</h6></div>
|
|
35
|
+
<div v-for="type of types" :keys="type.Type">
|
|
36
|
+
<a class="dropdown-item d-flex align-items-center" @click.prevent="createNewField(type)">
|
|
37
|
+
<itf-icon :name="type.Icon" :size="16" class="me-1" />
|
|
38
|
+
{{ type.Name }}
|
|
39
|
+
</a>
|
|
46
40
|
</div>
|
|
47
|
-
</
|
|
41
|
+
</itf-dropdown>
|
|
48
42
|
</div>
|
|
49
|
-
</sortable>
|
|
50
43
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
Add property
|
|
44
|
+
<itf-button small class="text-muted" @click="toggleShowAll">
|
|
45
|
+
<itf-icon :name="isShowAll ? 'chevron_up' : 'chevron_down'" />
|
|
46
|
+
{{isShowAll ? $tc('components.customize.hideMoreProperties', 1, { n: 1 }) : $tc('components.customize.showMoreProperties', 1, { n: 1 })}}
|
|
55
47
|
</itf-button>
|
|
56
48
|
</div>
|
|
57
49
|
</div>
|
|
@@ -79,55 +71,6 @@
|
|
|
79
71
|
display: flex;
|
|
80
72
|
flex-direction: column;
|
|
81
73
|
padding: 5px 3px 5px 15px;
|
|
82
|
-
|
|
83
|
-
&__draghandler {
|
|
84
|
-
user-select: none;
|
|
85
|
-
transition: opacity 150ms ease-in 0s;
|
|
86
|
-
cursor: grab;
|
|
87
|
-
display: flex;
|
|
88
|
-
align-items: center;
|
|
89
|
-
justify-content: center;
|
|
90
|
-
width: 18px;
|
|
91
|
-
height: 24px;
|
|
92
|
-
opacity: 0;
|
|
93
|
-
border-radius: 3px;
|
|
94
|
-
fill: rgba(55, 53, 47, 0.35);
|
|
95
|
-
}
|
|
96
|
-
&__inner:hover &__draghandler {
|
|
97
|
-
opacity: 1;
|
|
98
|
-
}
|
|
99
|
-
&__inner {
|
|
100
|
-
display: flex;
|
|
101
|
-
padding-bottom: 4px;
|
|
102
|
-
width: 100%;
|
|
103
|
-
}
|
|
104
|
-
&__name, &__value {
|
|
105
|
-
cursor: pointer;
|
|
106
|
-
display: flex;
|
|
107
|
-
align-items: center;
|
|
108
|
-
height: 100%;
|
|
109
|
-
width: 100%;
|
|
110
|
-
border-radius: 3px;
|
|
111
|
-
padding: 0 6px;
|
|
112
|
-
}
|
|
113
|
-
&__name {
|
|
114
|
-
display: flex;
|
|
115
|
-
align-items: center;
|
|
116
|
-
height: 34px;
|
|
117
|
-
width: 160px;
|
|
118
|
-
flex: 0 0 auto;
|
|
119
|
-
}
|
|
120
|
-
&__icon {
|
|
121
|
-
width: 22px;
|
|
122
|
-
margin-left: -22px;
|
|
123
|
-
opacity: 1;
|
|
124
|
-
}
|
|
125
|
-
&__value {
|
|
126
|
-
display: flex;
|
|
127
|
-
margin-left: 4px;
|
|
128
|
-
height: 100%;
|
|
129
|
-
min-width: 0;
|
|
130
|
-
}
|
|
131
74
|
}
|
|
132
75
|
</style>
|
|
133
76
|
<script>
|
|
@@ -138,11 +81,11 @@ import itfDropdown from '../dropdown/Dropdown.vue';
|
|
|
138
81
|
import itfLabel from '../form/Label.vue';
|
|
139
82
|
import itfTextField from '../text-field/TextField.vue';
|
|
140
83
|
import Sortable from '../sortable/Sortable';
|
|
141
|
-
import
|
|
142
|
-
import
|
|
143
|
-
import PropertyInlineEdit from './PropertyInlineEdit.vue';
|
|
84
|
+
import PropertyItem from './PropertyItem';
|
|
85
|
+
import { INLINE_TYPES } from './constants';
|
|
144
86
|
|
|
145
87
|
export default @Component({
|
|
88
|
+
name: 'itfPropertiesList',
|
|
146
89
|
components: {
|
|
147
90
|
itfIcon,
|
|
148
91
|
itfDropdown,
|
|
@@ -150,20 +93,24 @@ export default @Component({
|
|
|
150
93
|
itfLabel,
|
|
151
94
|
itfButton,
|
|
152
95
|
Sortable,
|
|
153
|
-
|
|
154
|
-
PropertiesEditMenu,
|
|
155
|
-
PropertyInlineEdit
|
|
96
|
+
PropertyItem
|
|
156
97
|
}
|
|
157
98
|
})
|
|
158
99
|
class itfPropertiesList extends Vue {
|
|
159
100
|
@Model('input') value;
|
|
160
101
|
@PropSync('fields') list;
|
|
161
102
|
@Prop({ type: Boolean, default: false }) loading;
|
|
103
|
+
@Prop({ type: Boolean, default: false }) editable;
|
|
104
|
+
@Prop({ type: Boolean, default: false }) lockFields;
|
|
162
105
|
|
|
163
|
-
|
|
106
|
+
isShowAll = false;
|
|
164
107
|
|
|
165
|
-
|
|
166
|
-
this.
|
|
108
|
+
toggleShowAll() {
|
|
109
|
+
this.isShowAll = !this.isShowAll;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
get types() {
|
|
113
|
+
return INLINE_TYPES;
|
|
167
114
|
}
|
|
168
115
|
|
|
169
116
|
onChange(item, index) {
|
|
@@ -172,8 +119,51 @@ class itfPropertiesList extends Vue {
|
|
|
172
119
|
this.$emit('update:fields', list);
|
|
173
120
|
}
|
|
174
121
|
|
|
175
|
-
|
|
176
|
-
|
|
122
|
+
onDelete(item) {
|
|
123
|
+
const list = [...this.list].filter((i) => i.Id !== item.Id);
|
|
124
|
+
this.$emit('update:fields', list);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
onDuplicate(item) {
|
|
128
|
+
const list = [...this.list];
|
|
129
|
+
const newItem = { ...item };
|
|
130
|
+
newItem.Id = `new-${Math.random().toString(36).substr(2, 9)}`;
|
|
131
|
+
const index = newItem.Name.match(/\((\d+)\)$/);
|
|
132
|
+
// додає вкінці номер (1), (2) і т.д.
|
|
133
|
+
newItem.Name = newItem.Name.replace(/\s\(\d+\)$/, '');
|
|
134
|
+
newItem.Name = `${newItem.Name}${index ? ` (${parseInt(index[1], 10) + 1})` : ' (1)'}`;
|
|
135
|
+
list.push(newItem);
|
|
136
|
+
this.$emit('update:fields', list);
|
|
137
|
+
|
|
138
|
+
this.$nextTick(() => {
|
|
139
|
+
for (const prop of this.$refs.properties) {
|
|
140
|
+
prop.hideEditMenu();
|
|
141
|
+
}
|
|
142
|
+
setTimeout(() => {
|
|
143
|
+
this.$refs.properties[list.length - 1].showEditMenu();
|
|
144
|
+
}, 10);
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
createNewField(type) {
|
|
149
|
+
const list = [...this.list];
|
|
150
|
+
list.push({
|
|
151
|
+
Id: `new-${Math.random().toString(36).substr(2, 9)}`,
|
|
152
|
+
Name: type.Name,
|
|
153
|
+
Type: type.Type,
|
|
154
|
+
Icon: type.Icon,
|
|
155
|
+
Visible: 'show'
|
|
156
|
+
});
|
|
157
|
+
this.$emit('update:fields', list);
|
|
158
|
+
if (this.$refs.newItemDd) {
|
|
159
|
+
this.$refs.newItemDd.hide();
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
this.$nextTick(() => {
|
|
163
|
+
setTimeout(() => {
|
|
164
|
+
this.$refs.properties[list.length - 1].showEditMenu();
|
|
165
|
+
}, 10);
|
|
166
|
+
});
|
|
177
167
|
}
|
|
178
168
|
}
|
|
179
169
|
</script>
|
|
@@ -5,14 +5,14 @@
|
|
|
5
5
|
<div>
|
|
6
6
|
<a class="dropdown-item" @click.prevent><itf-icon name="eye_no" /> {{ $t('components.customize.hideProperty') }}</a>
|
|
7
7
|
<ul class="itf-dropdown__menu shadow dropdown-menu dropdown-submenu">
|
|
8
|
-
<li><a class="dropdown-item" href="
|
|
9
|
-
<li><a class="dropdown-item" href="
|
|
10
|
-
<li><a class="dropdown-item" href="
|
|
8
|
+
<li><a class="dropdown-item" href="" @click.prevent="$emit('visibility', 'show')">{{ $t('components.customize.alwaysShow') }}<itf-icon v-if="field.Visible === 'show'" name="check" /></a></li>
|
|
9
|
+
<li><a class="dropdown-item" href="" @click.prevent="$emit('visibility', 'filled')">{{ $t('components.customize.hideWhenEmpty') }}<itf-icon v-if="field.Visible === 'filled'" name="check" /></a></li>
|
|
10
|
+
<li><a class="dropdown-item" href="" @click.prevent="$emit('visibility', 'hide')">{{ $t('components.customize.alwaysHide') }}<itf-icon v-if="field.Visible === 'hide'" name="check" /></a></li>
|
|
11
11
|
</ul>
|
|
12
12
|
</div>
|
|
13
13
|
<div><a class="dropdown-item" @click.prevent="$emit('duplicate')"><itf-icon name="duplicate" /> {{ $t('components.customize.duplicateProperty') }}</a></div>
|
|
14
14
|
<div>
|
|
15
|
-
<itf-delete-confirm-modal class="me-1"
|
|
15
|
+
<itf-delete-confirm-modal class="me-1" @delete="$emit('delete')">
|
|
16
16
|
<template #activator="{ on }">
|
|
17
17
|
<a class="dropdown-item text-danger" @click.prevent="on.click"><itf-icon name="trash" /> {{ $t('components.customize.deleteProperty') }}</a>
|
|
18
18
|
</template>
|
|
@@ -42,5 +42,6 @@ export default @Component({
|
|
|
42
42
|
})
|
|
43
43
|
|
|
44
44
|
class PropertiesEditMenu extends Vue {
|
|
45
|
+
@Prop() field;
|
|
45
46
|
}
|
|
46
47
|
</script>
|
|
@@ -1,46 +1,44 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<div
|
|
3
|
-
<
|
|
2
|
+
<div>
|
|
3
|
+
<component
|
|
4
|
+
v-if="component"
|
|
5
|
+
:is="component"
|
|
6
|
+
:value="value"
|
|
7
|
+
:field="field"
|
|
8
|
+
:focused="focused"
|
|
9
|
+
:editable="editable"
|
|
10
|
+
@input="$emit('input', $event)"
|
|
11
|
+
@focus="$emit('focus')"
|
|
12
|
+
@blur="$emit('blur')"
|
|
13
|
+
/>
|
|
4
14
|
</div>
|
|
5
15
|
</template>
|
|
6
|
-
<style lang="scss">
|
|
7
|
-
.itf-inline-edit {
|
|
8
|
-
textarea {
|
|
9
|
-
border: 0 none;
|
|
10
|
-
margin: 0;
|
|
11
|
-
outline: 0;
|
|
12
|
-
resize: none;
|
|
13
|
-
}
|
|
14
|
-
}
|
|
15
|
-
</style>
|
|
16
16
|
<script>
|
|
17
|
-
import { Vue, Component, Model,
|
|
18
|
-
import itfTextField from '../text-field/TextField.vue';
|
|
17
|
+
import { Vue, Component, Model, Prop } from 'vue-property-decorator';
|
|
19
18
|
import itfIcon from '../icon/Icon';
|
|
20
19
|
import itfButton from '../button/Button';
|
|
21
|
-
import
|
|
20
|
+
import { INLINE_TYPES } from './constants';
|
|
22
21
|
|
|
23
22
|
export default @Component({
|
|
23
|
+
name: 'itfPropertyInlineEdit',
|
|
24
24
|
components: {
|
|
25
|
-
itfButton,
|
|
26
25
|
itfIcon,
|
|
27
|
-
|
|
28
|
-
itfDeleteConfirmModal
|
|
26
|
+
itfButton,
|
|
29
27
|
}
|
|
30
28
|
})
|
|
31
29
|
|
|
32
|
-
class
|
|
30
|
+
class itfPropertyInlineEdit extends Vue {
|
|
33
31
|
@Model('input') value;
|
|
32
|
+
@Prop() field;
|
|
33
|
+
@Prop(Boolean) focused;
|
|
34
|
+
@Prop(Boolean) editable;
|
|
34
35
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
onFocus() {
|
|
43
|
-
this.isFocused = true;
|
|
36
|
+
get component() {
|
|
37
|
+
if (!this.field) {
|
|
38
|
+
return null;
|
|
39
|
+
}
|
|
40
|
+
const type = INLINE_TYPES.find(({ Type }) => Type === this.field.Type);
|
|
41
|
+
return type ? type.Component : null;
|
|
44
42
|
}
|
|
45
43
|
}
|
|
46
44
|
</script>
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div class="b-properties-list__inner">
|
|
3
|
+
<div class="b-properties-list__name" :class="{'editable': editable}">
|
|
4
|
+
<div class="b-properties-list__icon">
|
|
5
|
+
<div v-if="editable && !lockFields" role="button" tabindex="-1" class="b-properties-list__draghandler" sortable>
|
|
6
|
+
<itf-icon class="dragHandle" name="drag_vertical" />
|
|
7
|
+
</div>
|
|
8
|
+
</div>
|
|
9
|
+
|
|
10
|
+
<itf-dropdown :disabled="!editable || lockFields" ref="editDd" autoclose="outside" class="flex-grow-1 mw-100" shadow :button-options="{ icon: true, block: true }" @close="onClose">
|
|
11
|
+
<template #button>
|
|
12
|
+
<div class="d-flex align-items-center">
|
|
13
|
+
<itf-icon :name="field.Icon" :size="16" class="me-1" />
|
|
14
|
+
|
|
15
|
+
<div style="white-space: nowrap; overflow: hidden; text-overflow: ellipsis;" v-text="field.Name" />
|
|
16
|
+
</div>
|
|
17
|
+
</template>
|
|
18
|
+
<properties-edit-menu
|
|
19
|
+
v-if="isEditMode"
|
|
20
|
+
sortable-skip
|
|
21
|
+
:value="field"
|
|
22
|
+
:loading="loading"
|
|
23
|
+
@input="onChange($event)"
|
|
24
|
+
@delete="$emit('delete')"
|
|
25
|
+
@duplicate="$emit('duplicate')"
|
|
26
|
+
/>
|
|
27
|
+
<properties-popup-menu
|
|
28
|
+
v-else
|
|
29
|
+
sortable-skip
|
|
30
|
+
:field="field"
|
|
31
|
+
:loading="loading"
|
|
32
|
+
@edit="isEditMode = true"
|
|
33
|
+
@delete="$emit('delete')"
|
|
34
|
+
@duplicate="$emit('duplicate')"
|
|
35
|
+
@customize="$emit('customize')"
|
|
36
|
+
@visibility="onVisibilityChange(field, $event)"
|
|
37
|
+
/>
|
|
38
|
+
</itf-dropdown>
|
|
39
|
+
</div>
|
|
40
|
+
<div class="b-properties-list__value rounded-2" :class="{'editable': editable, 'active shadow rounded-2 bg-body': focusId === field.Id}" sortable-skip>
|
|
41
|
+
<property-inline-edit
|
|
42
|
+
@focus="onFocus(field.Id)"
|
|
43
|
+
@blur="onBlur(field.Id)"
|
|
44
|
+
class="flex-grow-1 b-properties-list__inline-editor"
|
|
45
|
+
:field="field"
|
|
46
|
+
:value="value"
|
|
47
|
+
:editable="editable"
|
|
48
|
+
:focused="focusId === field.Id"
|
|
49
|
+
@input="$emit('input', $event)"
|
|
50
|
+
/>
|
|
51
|
+
</div>
|
|
52
|
+
</div>
|
|
53
|
+
</template>
|
|
54
|
+
<style lang="scss">
|
|
55
|
+
.b-properties-list {
|
|
56
|
+
&__draghandler {
|
|
57
|
+
user-select: none;
|
|
58
|
+
transition: opacity 150ms ease-in 0s;
|
|
59
|
+
cursor: grab;
|
|
60
|
+
display: flex;
|
|
61
|
+
align-items: center;
|
|
62
|
+
justify-content: center;
|
|
63
|
+
width: 18px;
|
|
64
|
+
height: 24px;
|
|
65
|
+
opacity: 0;
|
|
66
|
+
border-radius: 3px;
|
|
67
|
+
fill: rgba(55, 53, 47, 0.35);
|
|
68
|
+
}
|
|
69
|
+
&__inner:hover &__draghandler {
|
|
70
|
+
opacity: 1;
|
|
71
|
+
}
|
|
72
|
+
&__inner {
|
|
73
|
+
display: flex;
|
|
74
|
+
padding-bottom: 4px;
|
|
75
|
+
width: 100%;
|
|
76
|
+
}
|
|
77
|
+
&__name, &__value {
|
|
78
|
+
display: flex;
|
|
79
|
+
align-items: center;
|
|
80
|
+
height: 100%;
|
|
81
|
+
width: 100%;
|
|
82
|
+
border-radius: 3px;
|
|
83
|
+
padding: 0 6px;
|
|
84
|
+
|
|
85
|
+
&.editable {
|
|
86
|
+
cursor: pointer;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
&__name {
|
|
90
|
+
display: flex;
|
|
91
|
+
align-items: center;
|
|
92
|
+
height: 34px;
|
|
93
|
+
width: 250px;
|
|
94
|
+
flex: 0 0 auto;
|
|
95
|
+
}
|
|
96
|
+
&__icon {
|
|
97
|
+
width: 22px;
|
|
98
|
+
margin-left: -22px;
|
|
99
|
+
opacity: 1;
|
|
100
|
+
}
|
|
101
|
+
&__value {
|
|
102
|
+
display: flex;
|
|
103
|
+
margin-left: 4px;
|
|
104
|
+
height: 100%;
|
|
105
|
+
min-width: 0;
|
|
106
|
+
padding: 0.25rem;
|
|
107
|
+
min-height: 34px;
|
|
108
|
+
|
|
109
|
+
&.editable &:hover, &.editable.active {
|
|
110
|
+
background-color: rgba(55, 53, 47, 0.08);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
</style>
|
|
115
|
+
<script>
|
|
116
|
+
import { Vue, Component, Model, Prop, PropSync } from 'vue-property-decorator';
|
|
117
|
+
import itfIcon from '../icon/Icon';
|
|
118
|
+
import itfButton from '../button/Button';
|
|
119
|
+
import itfDropdown from '../dropdown/Dropdown.vue';
|
|
120
|
+
import itfLabel from '../form/Label.vue';
|
|
121
|
+
import itfTextField from '../text-field/TextField.vue';
|
|
122
|
+
import Sortable from '../sortable/Sortable';
|
|
123
|
+
import PropertiesPopupMenu from './PropertiesPopupMenu.vue';
|
|
124
|
+
import PropertiesEditMenu from './PropertiesEditMenu.vue';
|
|
125
|
+
import PropertyInlineEdit from './PropertyInlineEdit.vue';
|
|
126
|
+
import { INLINE_TYPES } from './constants';
|
|
127
|
+
|
|
128
|
+
export default @Component({
|
|
129
|
+
name: 'itfPropertyItem',
|
|
130
|
+
components: {
|
|
131
|
+
itfIcon,
|
|
132
|
+
itfDropdown,
|
|
133
|
+
itfTextField,
|
|
134
|
+
itfLabel,
|
|
135
|
+
itfButton,
|
|
136
|
+
Sortable,
|
|
137
|
+
PropertiesPopupMenu,
|
|
138
|
+
PropertiesEditMenu,
|
|
139
|
+
PropertyInlineEdit
|
|
140
|
+
}
|
|
141
|
+
})
|
|
142
|
+
class itfPropertyItem extends Vue {
|
|
143
|
+
@Model('input') value;
|
|
144
|
+
@Prop() field;
|
|
145
|
+
@Prop({ type: Boolean, default: false }) loading;
|
|
146
|
+
@Prop({ type: Boolean, default: false }) editable;
|
|
147
|
+
@Prop({ type: Boolean, default: false }) lockFields;
|
|
148
|
+
|
|
149
|
+
isEditMode = false;
|
|
150
|
+
focusId = null;
|
|
151
|
+
|
|
152
|
+
get types() {
|
|
153
|
+
return INLINE_TYPES;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
onFocus(id) {
|
|
157
|
+
if (!this.editable) {
|
|
158
|
+
return;
|
|
159
|
+
}
|
|
160
|
+
this.focusId = id;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
onBlur() {
|
|
164
|
+
this.focusId = null;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
onClose() {
|
|
168
|
+
this.isEditMode = false;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
onChange(item) {
|
|
172
|
+
this.$emit('update:field', item);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
onVisibilityChange(field, value) {
|
|
176
|
+
const newField = { ...field };
|
|
177
|
+
newField.Visible = value;
|
|
178
|
+
this.$emit('update:field', newField);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
hideEditMenu() {
|
|
182
|
+
this.$refs.editDd.hide();
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
showEditMenu() {
|
|
186
|
+
this.$refs.editDd.show();
|
|
187
|
+
this.$nextTick(() => {
|
|
188
|
+
this.isEditMode = true;
|
|
189
|
+
});
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
</script>
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import InlineText from './Inline/Text';
|
|
2
|
+
import InlineDate from './Inline/Date';
|
|
3
|
+
import InlineMultiselect from './Inline/Multiselect.vue';
|
|
4
|
+
|
|
5
|
+
export const INLINE_TYPES = [
|
|
6
|
+
{ Type: 'text', Name: 'Text', Icon: 'type_text', Component: InlineText },
|
|
7
|
+
{ Type: 'date', Name: 'Date', Icon: 'type_date', Component: InlineDate },
|
|
8
|
+
{ Type: 'multiselect', Name: 'Multiselect', Icon: 'type_multiselect', Component: InlineMultiselect },
|
|
9
|
+
];
|
|
@@ -27,8 +27,8 @@ storiesOf('Common', module)
|
|
|
27
27
|
'2021-10-21': { text: '🎉', class: 'test' }
|
|
28
28
|
},
|
|
29
29
|
list: [
|
|
30
|
-
{ Id: 'test1', Name: '
|
|
31
|
-
{ Id: 'test2', Name: 'Test2', Icon: 'eye' },
|
|
30
|
+
{ Id: 'test1', Name: 'Last perfomance appraisal date', Icon: 'eye', Type: 'text' },
|
|
31
|
+
{ Id: 'test2', Name: 'Test2', Icon: 'eye', Type: 'date' },
|
|
32
32
|
],
|
|
33
33
|
value: {
|
|
34
34
|
test1: 'test1'
|
|
@@ -58,7 +58,9 @@ storiesOf('Common', module)
|
|
|
58
58
|
|
|
59
59
|
{{value}}
|
|
60
60
|
|
|
61
|
-
<itf-properties-list :fields.sync="list" v-model="value"></itf-properties-list>
|
|
61
|
+
<itf-properties-list editable :fields.sync="list" v-model="value"></itf-properties-list>
|
|
62
|
+
|
|
63
|
+
<itf-properties-list lock-fields editable :fields.sync="list" v-model="value"></itf-properties-list>
|
|
62
64
|
|
|
63
65
|
</itf-app>
|
|
64
66
|
</div>`,
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
|
|
3
3
|
<div class="itf-dropdown" :class="`drop${placement}`">
|
|
4
|
+
<div v-if="disabled"><slot name="button">{{label}}</slot></div>
|
|
4
5
|
<itf-button
|
|
6
|
+
v-else
|
|
5
7
|
:class="{ 'dropdown-toggle': toggle }"
|
|
6
8
|
v-bind="buttonOptions"
|
|
7
9
|
ref="toggle"
|
|
@@ -41,6 +43,7 @@ class itfDropdown extends Vue {
|
|
|
41
43
|
@Prop({ type: Boolean }) right;
|
|
42
44
|
@Prop({ type: Boolean }) toggle;
|
|
43
45
|
@Prop({ type: Boolean }) shadow;
|
|
46
|
+
@Prop({ type: Boolean }) disabled;
|
|
44
47
|
@Prop({ validator: (value) => [true, false, 'inside', 'outside'].includes(value), default: true }) autoclose;
|
|
45
48
|
@Prop({ type: Object, default: () => ({}) }) buttonOptions;
|
|
46
49
|
|
|
@@ -69,7 +72,7 @@ class itfDropdown extends Vue {
|
|
|
69
72
|
}
|
|
70
73
|
|
|
71
74
|
async mounted() {
|
|
72
|
-
if (typeof window === 'undefined') {
|
|
75
|
+
if (typeof window === 'undefined' || !this.$refs.toggle) {
|
|
73
76
|
return;
|
|
74
77
|
}
|
|
75
78
|
const { default: Dropdown } = await import('bootstrap/js/src/dropdown.js');
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
<style lang="scss">
|
|
2
|
+
.mdi-icon {
|
|
3
|
+
align-items: center;
|
|
4
|
+
display: inline-flex;
|
|
5
|
+
font-feature-settings: "liga";
|
|
6
|
+
font-size: 24px;
|
|
7
|
+
justify-content: center;
|
|
8
|
+
letter-spacing: normal;
|
|
9
|
+
line-height: 1;
|
|
10
|
+
position: relative;
|
|
11
|
+
text-indent: 0;
|
|
12
|
+
transition: 0.3s cubic-bezier(0.25, 0.8, 0.5, 1), visibility 0s;
|
|
13
|
+
vertical-align: middle;
|
|
14
|
+
-webkit-user-select: none;
|
|
15
|
+
-moz-user-select: none;
|
|
16
|
+
-ms-user-select: none;
|
|
17
|
+
user-select: none;
|
|
18
|
+
|
|
19
|
+
&--left {
|
|
20
|
+
margin-right: 5px;
|
|
21
|
+
}
|
|
22
|
+
&--right {
|
|
23
|
+
margin-left: 5px;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
</style>
|
|
27
|
+
<script>
|
|
28
|
+
import { Vue, Component } from 'vue-property-decorator';
|
|
29
|
+
|
|
30
|
+
export default @Component({
|
|
31
|
+
functional: true,
|
|
32
|
+
name: 'mdiIcon'
|
|
33
|
+
})
|
|
34
|
+
class mdiIcon extends Vue {
|
|
35
|
+
render (createElement, { data, slots, children, props, listeners }) {
|
|
36
|
+
const defaultSlot = slots().default;
|
|
37
|
+
if (!defaultSlot) {
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
const [node] = defaultSlot;
|
|
41
|
+
const icon = (node.text || '').trim();
|
|
42
|
+
let alignClass = '';
|
|
43
|
+
if (typeof props.dark !== 'undefined') {
|
|
44
|
+
alignClass = 'text-white';
|
|
45
|
+
}
|
|
46
|
+
if (typeof props.left !== 'undefined') {
|
|
47
|
+
alignClass = 'mdi-icon--left';
|
|
48
|
+
}
|
|
49
|
+
if (typeof props.right !== 'undefined') {
|
|
50
|
+
alignClass = 'mdi-icon--right';
|
|
51
|
+
}
|
|
52
|
+
const attrs = {
|
|
53
|
+
staticClass: `mdi-icon notranslate mdi ${icon} ${alignClass} ${data.staticClass || ''}`,
|
|
54
|
+
on: {
|
|
55
|
+
click: e => listeners.click && listeners.click(e)
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
if (typeof props.small !== 'undefined') {
|
|
59
|
+
attrs.style = { 'font-size': '16px' };
|
|
60
|
+
}
|
|
61
|
+
if (typeof props.size !== 'undefined') {
|
|
62
|
+
attrs.style = { 'font-size': `${props.size}px` };
|
|
63
|
+
}
|
|
64
|
+
return createElement(
|
|
65
|
+
'i',
|
|
66
|
+
attrs
|
|
67
|
+
);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
</script>
|