@itfin/components 1.2.96 → 1.2.98
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 +9 -9
- package/src/assets/scss/_css_variables.scss +11 -0
- package/src/components/datepicker/DatePicker.vue +11 -0
- package/src/components/datepicker/DateRangePicker.vue +2 -0
- package/src/components/dropdown/Dropdown.vue +12 -1
- package/src/components/editable/EditableElement.vue +112 -0
- package/src/components/editable/index.stories.js +53 -0
- package/src/components/table/Sortable.js +288 -0
- package/src/components/table/Table2.vue +85 -20
- package/src/components/table/TableBody.vue +67 -25
- package/src/components/table/TableGroup.vue +171 -81
- package/src/components/table/TableHeader.vue +189 -58
- package/src/components/table/draggable.js +161 -0
- package/src/components/table/event.js +57 -0
- package/src/components/table/index.stories.js +80 -5
- package/src/locales/uk.js +36 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@itfin/components",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.98",
|
|
4
4
|
"author": "Vitalii Savchuk <esvit666@gmail.com>",
|
|
5
5
|
"scripts": {
|
|
6
6
|
"serve": "vue-cli-service serve",
|
|
@@ -26,21 +26,22 @@
|
|
|
26
26
|
"@vue/cli-service": "^5.0.1",
|
|
27
27
|
"@vue/composition-api": "^1.7.1",
|
|
28
28
|
"air-datepicker": "^3.3.5",
|
|
29
|
-
"bootstrap": "
|
|
30
|
-
"core-js": "^3.
|
|
29
|
+
"bootstrap": "^5.2.3",
|
|
30
|
+
"core-js": "^3.7.0",
|
|
31
31
|
"debug": "^4.2.0",
|
|
32
32
|
"intersection-observer": "^0.12.2",
|
|
33
33
|
"lodash": "^4.17.20",
|
|
34
34
|
"luxon": "^3.3.0",
|
|
35
|
-
"pdfjs-dist": "^
|
|
35
|
+
"pdfjs-dist": "^2.10.377",
|
|
36
36
|
"tippy.js": "^6.3.2",
|
|
37
|
+
"vue": "^2.6.12",
|
|
37
38
|
"vue-imask": "^6.6.3",
|
|
38
39
|
"vue-property-decorator": "^9.1.2",
|
|
39
40
|
"vue-swatches": "^2.1.1",
|
|
40
41
|
"vue-virtual-scroller": "^1.1.2"
|
|
41
42
|
},
|
|
42
43
|
"devDependencies": {
|
|
43
|
-
"@babel/eslint-parser": "^7.
|
|
44
|
+
"@babel/eslint-parser": "^7.19.1",
|
|
44
45
|
"@babel/plugin-proposal-numeric-separator": "^7.18.6",
|
|
45
46
|
"@babel/plugin-syntax-numeric-separator": "^7.10.4",
|
|
46
47
|
"@storybook/addon-docs": "=6.3.8",
|
|
@@ -52,15 +53,14 @@
|
|
|
52
53
|
"@vue/eslint-config-airbnb": "^7.0.0",
|
|
53
54
|
"@vue/test-utils": "^1.1.1",
|
|
54
55
|
"babel-eslint": "^10.1.0",
|
|
55
|
-
"eslint": "^8.
|
|
56
|
-
"eslint-plugin-import": "^2.
|
|
56
|
+
"eslint": "^8.30.0",
|
|
57
|
+
"eslint-plugin-import": "^2.22.1",
|
|
57
58
|
"eslint-plugin-prettier": "^4.2.1",
|
|
58
|
-
"eslint-plugin-vue": "^9.
|
|
59
|
+
"eslint-plugin-vue": "^9.9.0",
|
|
59
60
|
"fibers": "^5.0.0",
|
|
60
61
|
"marked": "^4.2.5",
|
|
61
62
|
"sass": "^1.29.0",
|
|
62
63
|
"sass-loader": "^10.0.5",
|
|
63
|
-
"vue": "^2.6.12",
|
|
64
64
|
"vue-class-component": "^7.2.6",
|
|
65
65
|
"vue-eslint-parser": "^9.1.0",
|
|
66
66
|
"vue-template-compiler": "=2.6.14"
|
|
@@ -9,10 +9,21 @@ $color-outcome: #b91e1e;
|
|
|
9
9
|
--color-outcome: #{$color-outcome};
|
|
10
10
|
--color-primary: #{$primary};
|
|
11
11
|
--body-bg: #{$body-bg};
|
|
12
|
+
|
|
13
|
+
.modal-backdrop {
|
|
14
|
+
--bs-backdrop-bg: #{$body-bg};
|
|
15
|
+
--bs-backdrop-opacity: 0.75;
|
|
16
|
+
}
|
|
12
17
|
}
|
|
13
18
|
|
|
14
19
|
[data-theme="dark"] {
|
|
15
20
|
--color-primary: #{$dark-primary};
|
|
16
21
|
--body-bg: #{$dark-body-bg};
|
|
22
|
+
--bs-backdrop-bg: #{$dark-body-bg};
|
|
17
23
|
--color-primary-hover: #{darken($dark-primary, 10%)};
|
|
24
|
+
|
|
25
|
+
.modal-backdrop {
|
|
26
|
+
--bs-backdrop-opacity: 0.5;
|
|
27
|
+
--bs-backdrop-bg: #{$dark-body-bg};
|
|
28
|
+
}
|
|
18
29
|
}
|
|
@@ -21,6 +21,7 @@
|
|
|
21
21
|
:unmask="false"
|
|
22
22
|
:lazy="!focused"
|
|
23
23
|
:placeholder="placeholder"
|
|
24
|
+
:disabled="disabled"
|
|
24
25
|
/>
|
|
25
26
|
|
|
26
27
|
<div class="addon-end" v-if="clearable && value">
|
|
@@ -85,6 +86,7 @@ class itfDatePicker extends Vue {
|
|
|
85
86
|
@Prop({ type: String, default: 'bottom-start' }) placement;
|
|
86
87
|
@Prop({ type: [String, Date], default: '' }) minDate;
|
|
87
88
|
@Prop(Boolean) clearable;
|
|
89
|
+
@Prop(Boolean) disabled;
|
|
88
90
|
|
|
89
91
|
focused = false;
|
|
90
92
|
|
|
@@ -117,6 +119,9 @@ class itfDatePicker extends Vue {
|
|
|
117
119
|
}
|
|
118
120
|
|
|
119
121
|
mounted() {
|
|
122
|
+
if (this.disabled) {
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
120
125
|
// якщо в модалці, то контекст модалки, якщо ні, то аплікейшена
|
|
121
126
|
const context = this.$el.closest('.itf-append-context') || document.body;
|
|
122
127
|
this.tooltip = tippy(this.$refs.input.$el, {
|
|
@@ -183,10 +188,16 @@ class itfDatePicker extends Vue {
|
|
|
183
188
|
}
|
|
184
189
|
|
|
185
190
|
onFocus() {
|
|
191
|
+
if (this.disabled) {
|
|
192
|
+
return;
|
|
193
|
+
}
|
|
186
194
|
this.focused = true;
|
|
187
195
|
}
|
|
188
196
|
|
|
189
197
|
onBlur(e) {
|
|
198
|
+
if (this.disabled) {
|
|
199
|
+
return;
|
|
200
|
+
}
|
|
190
201
|
this.focused = false;
|
|
191
202
|
this.updateValue(e.target.value, !!e.target.value);
|
|
192
203
|
}
|
|
@@ -19,6 +19,7 @@
|
|
|
19
19
|
:mask="Date"
|
|
20
20
|
:pattern="dateFormat"
|
|
21
21
|
:blocks="blocks"
|
|
22
|
+
:disabled="disabled"
|
|
22
23
|
:format="format"
|
|
23
24
|
:parse="parse"
|
|
24
25
|
:unmask="false"
|
|
@@ -105,6 +106,7 @@ class itfDateRangePicker extends Vue {
|
|
|
105
106
|
@Prop({ type: String, default: 'bottom-start' }) placement;
|
|
106
107
|
@Prop({ type: [String, Date], default: '' }) minDate;
|
|
107
108
|
@Prop({ type: [String, Date], default: ''}) maxDate;
|
|
109
|
+
@Prop(Boolean) disabled;
|
|
108
110
|
|
|
109
111
|
focused = false;
|
|
110
112
|
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
<div class="itf-dropdown" :class="`drop${placement}`">
|
|
4
4
|
<div v-if="disabled"><slot name="button">{{label}}</slot></div>
|
|
5
5
|
<itf-button
|
|
6
|
-
v-else
|
|
6
|
+
v-else-if="!text"
|
|
7
7
|
:class="{ 'dropdown-toggle': toggle }"
|
|
8
8
|
v-bind="buttonOptions"
|
|
9
9
|
ref="toggle"
|
|
@@ -13,6 +13,9 @@
|
|
|
13
13
|
>
|
|
14
14
|
<slot name="button">{{label}}</slot>
|
|
15
15
|
</itf-button>
|
|
16
|
+
<div v-else :class="{ 'dropdown-toggle': toggle }" ref="toggle" :id="modalId" data-bs-toggle="dropdown" aria-expanded="false">
|
|
17
|
+
<slot name="button">{{label}}</slot>
|
|
18
|
+
</div>
|
|
16
19
|
<div
|
|
17
20
|
class="itf-dropdown__menu dropdown-menu"
|
|
18
21
|
:class="{'dropdown-menu-end': right, 'shadow': shadow}"
|
|
@@ -44,6 +47,8 @@ class itfDropdown extends Vue {
|
|
|
44
47
|
@Prop({ type: Boolean }) toggle;
|
|
45
48
|
@Prop({ type: Boolean }) shadow;
|
|
46
49
|
@Prop({ type: Boolean }) disabled;
|
|
50
|
+
@Prop({ type: Boolean }) text;
|
|
51
|
+
@Prop({ type: Boolean }) appendToBody;
|
|
47
52
|
@Prop({ validator: (value) => [true, false, 'inside', 'outside'].includes(value), default: true }) autoclose;
|
|
48
53
|
@Prop({ type: Object, default: () => ({}) }) buttonOptions;
|
|
49
54
|
|
|
@@ -80,6 +85,12 @@ class itfDropdown extends Vue {
|
|
|
80
85
|
reference: 'toggle',
|
|
81
86
|
autoClose: this.autoclose
|
|
82
87
|
});
|
|
88
|
+
let context = document.body;
|
|
89
|
+
if (this.appendToBody && this.$refs.dropdown instanceof Node && this.$refs.dropdown.parentNode) {
|
|
90
|
+
this.$refs.dropdown.parentNode.removeChild(this.$refs.dropdown);
|
|
91
|
+
context.appendChild(this.$refs.dropdown); // should append only to body
|
|
92
|
+
}
|
|
93
|
+
|
|
83
94
|
this.$el.addEventListener('shown.bs.dropdown', () => {
|
|
84
95
|
setTimeout(() => {
|
|
85
96
|
this.$emit('open');
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
|
|
3
|
+
<div>
|
|
4
|
+
<div ref="element">
|
|
5
|
+
<slot name="activator" :open="open">
|
|
6
|
+
<div @click.prevent.stop="open">
|
|
7
|
+
<slot :open="open" :hide="hide">
|
|
8
|
+
</slot>
|
|
9
|
+
</div>
|
|
10
|
+
</slot>
|
|
11
|
+
</div>
|
|
12
|
+
</div>
|
|
13
|
+
|
|
14
|
+
</template>
|
|
15
|
+
<style lang="scss">
|
|
16
|
+
.itf-editable-element {
|
|
17
|
+
z-index: 1060;
|
|
18
|
+
position: fixed;
|
|
19
|
+
}
|
|
20
|
+
</style>
|
|
21
|
+
<script>
|
|
22
|
+
import { Vue, Component, Prop, PropSync } from 'vue-property-decorator';
|
|
23
|
+
import itfButton from '../button/Button.vue';
|
|
24
|
+
import itfIcon from '../icon/Icon.vue';
|
|
25
|
+
import FocusTrap from "bootstrap/js/src/util/focustrap";
|
|
26
|
+
|
|
27
|
+
export default @Component({
|
|
28
|
+
name: 'itfEditableElement',
|
|
29
|
+
components: {
|
|
30
|
+
itfButton,
|
|
31
|
+
itfIcon,
|
|
32
|
+
}
|
|
33
|
+
})
|
|
34
|
+
class itfEditableElement extends Vue {
|
|
35
|
+
_backdrop = null;
|
|
36
|
+
_context = null;
|
|
37
|
+
_focusTrap = null;
|
|
38
|
+
_elContainer = null;
|
|
39
|
+
_tempContainer = null;
|
|
40
|
+
|
|
41
|
+
isOpen = false;
|
|
42
|
+
|
|
43
|
+
async mounted() {
|
|
44
|
+
this._context = this.$el.closest('.itf-append-context:not(.modal-content)') || document.body;
|
|
45
|
+
|
|
46
|
+
const { default: Backdrop } = await import('../modal/backdrop');
|
|
47
|
+
this._backdrop = new Backdrop({
|
|
48
|
+
rootElement: this._context,
|
|
49
|
+
isVisible: true,
|
|
50
|
+
isAnimated: true,
|
|
51
|
+
clickCallback: () => {
|
|
52
|
+
this.hide();
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
this._focusTrap = new FocusTrap({
|
|
56
|
+
trapElement: this.$el
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
open() {
|
|
61
|
+
if (this.isOpen) {
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
this.isOpen = true;
|
|
65
|
+
|
|
66
|
+
this.$emit('open');
|
|
67
|
+
this._backdrop.show();
|
|
68
|
+
this._focusTrap.activate();
|
|
69
|
+
|
|
70
|
+
this._tempContainer = document.createElement('div');
|
|
71
|
+
this.applyPosition(this._tempContainer, this.$refs.element)
|
|
72
|
+
this._tempContainer.classList.add('itf-editable-element');
|
|
73
|
+
this.$el.removeChild(this.$refs.element);
|
|
74
|
+
this._tempContainer.appendChild(this.$refs.element);
|
|
75
|
+
this._context.appendChild(this._tempContainer);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
applyPosition(container, el) {
|
|
79
|
+
const box = el.getBoundingClientRect();
|
|
80
|
+
const left = box.left + document.body.scrollLeft;
|
|
81
|
+
const top = box.top + document.body.scrollTop;
|
|
82
|
+
container.style.left = `${left}px`;
|
|
83
|
+
container.style.top = `${top}px`;
|
|
84
|
+
container.style.width = `${box.width}px`;
|
|
85
|
+
container.style.height = `${box.height}px`;
|
|
86
|
+
this.$el.style.width = `${box.width}px`;
|
|
87
|
+
this.$el.style.height = `${box.height}px`;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
hide() {
|
|
91
|
+
if (!this.isOpen) {
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
this._focusTrap.deactivate();
|
|
95
|
+
this._backdrop.hide(() => {
|
|
96
|
+
this._tempContainer.removeChild(this.$refs.element);
|
|
97
|
+
this.$el.appendChild(this.$refs.element);
|
|
98
|
+
this._context.removeChild(this._tempContainer); // remove the temporary div
|
|
99
|
+
|
|
100
|
+
this.$el.style.width = null;
|
|
101
|
+
this.$el.style.height = null;
|
|
102
|
+
this.isOpen = false;
|
|
103
|
+
this.$emit('hide');
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
beforeDestroy() {
|
|
108
|
+
this._backdrop.dispose();
|
|
109
|
+
this._focusTrap.deactivate();
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
</script>
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { storiesOf } from '@storybook/vue';
|
|
2
|
+
import itfButton from '../button/Button.vue';
|
|
3
|
+
import itfEditableElement from './EditableElement.vue';
|
|
4
|
+
|
|
5
|
+
storiesOf('Common', module)
|
|
6
|
+
.add('Editable element', () => ({
|
|
7
|
+
components: {
|
|
8
|
+
itfButton,
|
|
9
|
+
itfEditableElement
|
|
10
|
+
},
|
|
11
|
+
data() {
|
|
12
|
+
return {}
|
|
13
|
+
},
|
|
14
|
+
template: `<div>
|
|
15
|
+
<p>You need wrap whole application with this tag</p>
|
|
16
|
+
|
|
17
|
+
<h2>Usage</h2>
|
|
18
|
+
|
|
19
|
+
<pre>
|
|
20
|
+
<itf-table
|
|
21
|
+
:columns="columns"
|
|
22
|
+
:rows="list"
|
|
23
|
+
>
|
|
24
|
+
<template #column.Employee="{ item }"></template>
|
|
25
|
+
</itf-table>
|
|
26
|
+
</pre>
|
|
27
|
+
|
|
28
|
+
<h3>Example</h3>
|
|
29
|
+
|
|
30
|
+
<itf-editable-element>
|
|
31
|
+
<template v-slot="{ hide }">
|
|
32
|
+
|
|
33
|
+
<div class="card" style="width: 300px">
|
|
34
|
+
<div class="card-body">
|
|
35
|
+
asdadas
|
|
36
|
+
|
|
37
|
+
<a href="#" @click="hide">Close</a>
|
|
38
|
+
</div>
|
|
39
|
+
</div>
|
|
40
|
+
</template>
|
|
41
|
+
</itf-editable-element>
|
|
42
|
+
|
|
43
|
+
<!--itf-table
|
|
44
|
+
:columns="columns"
|
|
45
|
+
:rows="list"
|
|
46
|
+
>
|
|
47
|
+
<template #column.Employee="{ item }">
|
|
48
|
+
{{item.Employee}}
|
|
49
|
+
</template>
|
|
50
|
+
</itf-table-->
|
|
51
|
+
|
|
52
|
+
</div>`,
|
|
53
|
+
}));
|
|
@@ -0,0 +1,288 @@
|
|
|
1
|
+
import Vue from 'vue';
|
|
2
|
+
import { Draggable } from './draggable'
|
|
3
|
+
import DraggableEvent from './event'
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
Vue.directive("sortable-item", {
|
|
7
|
+
inserted(el, { value }) {
|
|
8
|
+
el.sortableItemPayload = value?.payload;
|
|
9
|
+
el.classList.add(draggableNode.options.draggableClass);
|
|
10
|
+
el.getAttribute("with-handle") && el.classList.add(draggableNode.options.dragHandleClass);
|
|
11
|
+
value?.mirror && (el.dataset.draggableMirror = JSON.stringify(value.mirror));
|
|
12
|
+
},
|
|
13
|
+
update(el, {value}) {
|
|
14
|
+
el.sortableItemPayload = value?.payload;
|
|
15
|
+
el.classList.add(draggableNode.options.draggableClass);
|
|
16
|
+
el.getAttribute("with-handle") && el.classList.add(draggableNode.options.dragHandleClass);
|
|
17
|
+
value?.mirror && (el.dataset.draggableMirror = JSON.stringify(value.mirror))
|
|
18
|
+
}
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
const SORT_TAG = "sort";
|
|
23
|
+
function someFunc({ source, over, overContainer, children }) {
|
|
24
|
+
const isDelete = !children.length
|
|
25
|
+
, isInContainer = source.parentNode !== overContainer
|
|
26
|
+
, Q = over && !isInContainer;
|
|
27
|
+
if (isDelete) {
|
|
28
|
+
return moveToContainer(source, overContainer)
|
|
29
|
+
}
|
|
30
|
+
return Q ? $(source, over) : isInContainer ? insertBefore(source, over, overContainer) : null
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function moveToContainer(el, newContainer) {
|
|
34
|
+
const oldContainer = el.parentNode;
|
|
35
|
+
newContainer.appendChild(el);
|
|
36
|
+
return {
|
|
37
|
+
oldContainer,
|
|
38
|
+
newContainer
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
function $(el, container) {
|
|
42
|
+
console.info('$')
|
|
43
|
+
const tt = indexOf(el)
|
|
44
|
+
, ot = indexOf(container);
|
|
45
|
+
if (tt < ot) {
|
|
46
|
+
el.parentNode.insertBefore(el, container.nextElementSibling);
|
|
47
|
+
} else {
|
|
48
|
+
el.parentNode.insertBefore(el, container);
|
|
49
|
+
}
|
|
50
|
+
return {
|
|
51
|
+
oldContainer: el.parentNode,
|
|
52
|
+
newContainer: el.parentNode
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
function indexOf(A) {
|
|
56
|
+
return Array.prototype.indexOf.call(A.parentNode.children, A)
|
|
57
|
+
}
|
|
58
|
+
function insertBefore(A, H, tt) {
|
|
59
|
+
console.info('insertBefore')
|
|
60
|
+
const ot = A.parentNode;
|
|
61
|
+
return H ? H.parentNode.insertBefore(A, H) : tt.appendChild(A),
|
|
62
|
+
{
|
|
63
|
+
oldContainer: ot,
|
|
64
|
+
newContainer: A.parentNode
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
function c(A, H, tt) {
|
|
68
|
+
const ot = [...A.slice(0, H), ...A.slice(H + 1, A.length)];
|
|
69
|
+
return [...ot.slice(0, tt), A[H], ...ot.slice(tt, ot.length)]
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export
|
|
73
|
+
const Sortable = {
|
|
74
|
+
name: 'Sortable',
|
|
75
|
+
render: function() {
|
|
76
|
+
return this._self._c(this.tag, {
|
|
77
|
+
ref: "sortable",
|
|
78
|
+
tag: "component"
|
|
79
|
+
}, [this._t("default", null, {
|
|
80
|
+
items: this.value
|
|
81
|
+
})], 2)
|
|
82
|
+
},
|
|
83
|
+
props: {
|
|
84
|
+
value: {
|
|
85
|
+
default: ()=>[],
|
|
86
|
+
type: Array
|
|
87
|
+
},
|
|
88
|
+
group: {
|
|
89
|
+
type: String,
|
|
90
|
+
default: "defaultGroup"
|
|
91
|
+
},
|
|
92
|
+
tag: {
|
|
93
|
+
type: String,
|
|
94
|
+
default: "div"
|
|
95
|
+
}
|
|
96
|
+
},
|
|
97
|
+
mounted() {
|
|
98
|
+
Draggable.addContainer(this.$refs.sortable);
|
|
99
|
+
this.$refs.sortable.dataset.group = this.group;
|
|
100
|
+
this.handler = event=>{
|
|
101
|
+
const H = [...this.value];
|
|
102
|
+
H.splice(event.detail.newIndex, 0, event.detail.item),
|
|
103
|
+
this.$emit("input", H),
|
|
104
|
+
this.$emit("receive", {
|
|
105
|
+
...event.detail,
|
|
106
|
+
newItems: H
|
|
107
|
+
})
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
this.$refs.sortable.addEventListener(SORT_TAG, this.handler);
|
|
111
|
+
Draggable.on("drag:start", this.onDragStart);
|
|
112
|
+
},
|
|
113
|
+
beforeDestroy() {
|
|
114
|
+
this.$refs.sortable.removeEventListener(SORT_TAG, this.handler)
|
|
115
|
+
},
|
|
116
|
+
destroyed() {
|
|
117
|
+
Draggable.removeContainer(this.$refs.sortable),
|
|
118
|
+
Draggable.off("drag:start", this.onDragStart),
|
|
119
|
+
this.destroyMirror()
|
|
120
|
+
},
|
|
121
|
+
methods: {
|
|
122
|
+
stopListening() {
|
|
123
|
+
Draggable
|
|
124
|
+
.off("drag:move", this.dragMove)
|
|
125
|
+
.off("drag:over:container", this.onDragOverContainer)
|
|
126
|
+
.off("drag:out:container", this.onDragOutContainer)
|
|
127
|
+
.off("drag:over", this.onDragOver)
|
|
128
|
+
.off("drag:stop", this.onDragStop)
|
|
129
|
+
.off("mirror:created", this.createMirror)
|
|
130
|
+
.off("mirror:destroy", this.destroyMirror);
|
|
131
|
+
this.destroyMirror();
|
|
132
|
+
},
|
|
133
|
+
createMirror(el) {
|
|
134
|
+
console.info(el, this.dragging)
|
|
135
|
+
if (this.dragging && this.$scopedSlots.mirror) {
|
|
136
|
+
const H = this.$scopedSlots.mirror({
|
|
137
|
+
item: this.source.item
|
|
138
|
+
});
|
|
139
|
+
const tt = Vue.ZP.extend({
|
|
140
|
+
parent: this,
|
|
141
|
+
render() {
|
|
142
|
+
return H
|
|
143
|
+
}
|
|
144
|
+
});
|
|
145
|
+
const Y = new tt().$mount();
|
|
146
|
+
this.mirrorComponent = Y;
|
|
147
|
+
el.mirror.innerHTML = "";
|
|
148
|
+
el.mirror.appendChild(Y.$el);
|
|
149
|
+
}
|
|
150
|
+
},
|
|
151
|
+
destroyMirror() {
|
|
152
|
+
if (this.mirrorComponent) {
|
|
153
|
+
this.mirrorComponent.$destroy();
|
|
154
|
+
this.mirrorComponent = null;
|
|
155
|
+
}
|
|
156
|
+
},
|
|
157
|
+
getDraggableElementsForContainer(el) {
|
|
158
|
+
return [...el.children].filter(item => item !== Draggable.originalSource && item !== Draggable.mirror)
|
|
159
|
+
},
|
|
160
|
+
index(el) {
|
|
161
|
+
return this.getDraggableElementsForContainer(el.parentNode).indexOf(el)
|
|
162
|
+
},
|
|
163
|
+
onDragStart(event) {
|
|
164
|
+
if (event.originalEvent.target.tagName === "INPUT") {
|
|
165
|
+
event.cancel();
|
|
166
|
+
return
|
|
167
|
+
}
|
|
168
|
+
if (event.sourceContainer !== this.$refs.sortable) {
|
|
169
|
+
return;
|
|
170
|
+
}
|
|
171
|
+
Draggable
|
|
172
|
+
.on("drag:move", this.dragMove)
|
|
173
|
+
.on("drag:over:container", this.onDragOverContainer)
|
|
174
|
+
.on("drag:out:container", this.onDragOutContainer)
|
|
175
|
+
.on("drag:over", this.onDragOver)
|
|
176
|
+
.on("drag:stop", this.onDragStop)
|
|
177
|
+
.on("mirror:created", this.createMirror)
|
|
178
|
+
.on("mirror:destroy", this.destroyMirror);
|
|
179
|
+
const oldIndex = this.index(event.source);
|
|
180
|
+
this.$emit("start");
|
|
181
|
+
this.oldIndex = oldIndex;
|
|
182
|
+
this.source = {
|
|
183
|
+
oldIndex,
|
|
184
|
+
item: this.value[oldIndex]
|
|
185
|
+
};
|
|
186
|
+
},
|
|
187
|
+
dragMove(el) {
|
|
188
|
+
Draggable.trigger(new DraggableEvent({
|
|
189
|
+
...el.data,
|
|
190
|
+
sourceComponent: this.$refs.sortable,
|
|
191
|
+
group: this.group,
|
|
192
|
+
draggablePayload: {
|
|
193
|
+
item: this.value[this.oldIndex],
|
|
194
|
+
...el.originalSource.sortableItemPayload
|
|
195
|
+
}
|
|
196
|
+
}))
|
|
197
|
+
},
|
|
198
|
+
onDragOverContainer(event) {
|
|
199
|
+
const {source, over, overContainer} = event;
|
|
200
|
+
if (overContainer.dataset.group !== this.group) {
|
|
201
|
+
return;
|
|
202
|
+
}
|
|
203
|
+
const children = this.getDraggableElementsForContainer(overContainer);
|
|
204
|
+
this.destinationContainer = overContainer;
|
|
205
|
+
|
|
206
|
+
console.info('onDragOverContainer', overContainer, children)
|
|
207
|
+
someFunc({
|
|
208
|
+
source,
|
|
209
|
+
over,
|
|
210
|
+
overContainer,
|
|
211
|
+
children
|
|
212
|
+
});
|
|
213
|
+
},
|
|
214
|
+
onDragOutContainer(el) {
|
|
215
|
+
this.destinationContainer = null;
|
|
216
|
+
const overContainer = this.$refs.sortable
|
|
217
|
+
, children = this.getDraggableElementsForContainer(overContainer)
|
|
218
|
+
, over = children[this.source.oldIndex];
|
|
219
|
+
someFunc({
|
|
220
|
+
source: el.source,
|
|
221
|
+
over,
|
|
222
|
+
overContainer,
|
|
223
|
+
children
|
|
224
|
+
})
|
|
225
|
+
},
|
|
226
|
+
onDragOver(A) {
|
|
227
|
+
const { source, over, overContainer } = A;
|
|
228
|
+
if (overContainer.dataset.group !== this.group || over === A.originalSource || over === source) {
|
|
229
|
+
return;
|
|
230
|
+
}
|
|
231
|
+
const Y = this.getDraggableElementsForContainer(overContainer);
|
|
232
|
+
if (Y.includes(over)) {
|
|
233
|
+
someFunc({
|
|
234
|
+
source: source,
|
|
235
|
+
over: over,
|
|
236
|
+
overContainer: overContainer,
|
|
237
|
+
children: Y
|
|
238
|
+
});
|
|
239
|
+
}
|
|
240
|
+
},
|
|
241
|
+
onDragStop(item) {
|
|
242
|
+
this.stopListening();
|
|
243
|
+
if (!this.destinationContainer || this.destinationContainer.dataset.group !== this.group) {
|
|
244
|
+
return;
|
|
245
|
+
}
|
|
246
|
+
const { source } = this;
|
|
247
|
+
source.newIndex = this.index(item.source);
|
|
248
|
+
source.sortableItemPayload = { ...item.originalSource.sortableItemPayload };
|
|
249
|
+
console.info(item.source, item.originalSource)
|
|
250
|
+
item.originalSource.parentNode.insertBefore(item.source, item.originalSource);
|
|
251
|
+
if (item.sourceContainer === this.destinationContainer) {
|
|
252
|
+
this.onSortItems(source);
|
|
253
|
+
} else {
|
|
254
|
+
this.onRemoveItem(source);
|
|
255
|
+
this.onReceiveItem(source, this.destinationContainer);
|
|
256
|
+
}
|
|
257
|
+
this.$emit("stop");
|
|
258
|
+
},
|
|
259
|
+
onSortItems(item) {
|
|
260
|
+
if (item.oldIndex === item.newIndex) {
|
|
261
|
+
return;
|
|
262
|
+
}
|
|
263
|
+
const newItems = c(this.value, item.oldIndex, item.newIndex);
|
|
264
|
+
this.$emit("input", newItems);
|
|
265
|
+
this.$emit("move", {
|
|
266
|
+
newItems,
|
|
267
|
+
...item
|
|
268
|
+
});
|
|
269
|
+
},
|
|
270
|
+
onReceiveItem(el, H) {
|
|
271
|
+
H.dispatchEvent(new CustomEvent(SORT_TAG,{
|
|
272
|
+
detail: {
|
|
273
|
+
item: el.item,
|
|
274
|
+
newIndex: el.newIndex,
|
|
275
|
+
oldIndex: el.oldIndex,
|
|
276
|
+
sortableItemPayload: el.sortableItemPayload
|
|
277
|
+
}
|
|
278
|
+
}))
|
|
279
|
+
},
|
|
280
|
+
onRemoveItem(el) {
|
|
281
|
+
const H = this.value.filter(tt=>tt !== el.item);
|
|
282
|
+
this.$emit("input", H)
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
};
|
|
286
|
+
|
|
287
|
+
|
|
288
|
+
Vue.component("Sortable", Sortable)
|