├── .gitignore
├── LICENSE.txt
├── README.md
├── bower.json
├── gulpfile.js
├── package.json
├── vue-dragula.es6.js
└── vue-dragula.min.js
/.gitignore:
--------------------------------------------------------------------------------
1 | bower_components
2 | node_modules
3 | jspm_packages
4 | logs
5 | npm-debug.log*
6 | pids
7 | lib-cov
8 | coverage
9 | build/Release
10 |
11 | .npm
12 | .nyc_output
13 | .grunt
14 | .lock-wscript
15 | .node_repl_history
16 |
17 | *.pid
18 | *.seed
19 |
20 | *.log
21 | *.sql
22 | *.sqlite
23 |
24 | *.7z
25 | *.dmg
26 | *.gz
27 | *.iso
28 | *.jar
29 | *.rar
30 | *.tar
31 | *.zip
32 |
33 | *.com
34 | *.class
35 | *.dll
36 | *.exe
37 | *.o
38 | *.so
--------------------------------------------------------------------------------
/LICENSE.txt:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2016 Marc Erdmann
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # vue-dragula
2 | ### Features
3 | - simple nested drag and drops
4 | - multiple dragula instances automatically
5 | - Vue.js keeps track of all changes
6 |
7 | ### Installation
8 | Either do:
9 | ```
10 | bower install vue-dragula
11 | ```
12 | or download the scripts and include them in your project.
13 |
14 | ### Usage
15 | Include vue-dragula after Vue.js and dragula:
16 | ```html
17 |
18 |
19 |
20 | ```
21 | ---
22 | Make your Vue.js components draggable by initializing with *this* and an options-object, which can contain a *handle* and a *direction*, e.g.:
23 | ```javascript
24 | Vue.component('dnd-board', {
25 | props: ['id'],
26 | template: '#dnd-board',
27 | ready: function() {
28 | Vue.dnd.initialize(this, {handle: 'class-of-handle', direction: 'horizontal'});
29 | },
30 | beforeDestroy: function () {
31 | Vue.dnd.destroy(this);
32 | }
33 | });
34 | ```
35 | - To pass the class of a handle or a direction parameter are optional.
36 | - The direction could be 'horizontal' or 'vertical'.
37 | - The default direction is 'vertical'.
38 | - In the above example child-elements of the component 'dnd-board' will be draggable.
39 |
40 | ---
41 |
42 | You have to make sure the component (e.g. 'dnd-board') has an id representing its draggable children's position in the Vue instance's data object, e.g.:
43 | ```html
44 |
45 |
46 |
47 | {{list.title}}
48 |
49 |
50 |
51 | {{todo.title}}
52 |
53 |
54 |
55 |
56 |
57 | ```
58 | When the Vue instance's data object is:
59 | ```javascript
60 | data: {
61 | lists: [
62 | {
63 | title: 'Todo',
64 | todos: [
65 | {title: 'code'},
66 | {title: 'eat'},
67 | {title: 'sleep'},
68 | {title: 'repeat'}
69 | ]
70 | },
71 | {
72 | title: 'Todo',
73 | todos: [
74 | {title: 'code'},
75 | {title: 'eat'},
76 | {title: 'sleep'},
77 | {title: 'repeat'}
78 | ]
79 | },
80 | {
81 | title: 'Todo',
82 | todos: [
83 | {title: 'code'},
84 | {title: 'eat'},
85 | {title: 'sleep'},
86 | {title: 'repeat'}
87 | ]
88 | }
89 | ]
90 | }
91 | ```
92 | ---
93 | If you have any questions or feedback feel free to open an issue :)
--------------------------------------------------------------------------------
/bower.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "vue-dragula",
3 | "description": "a vue.js plugin for dragula.js",
4 | "authors": [
5 | "Marc Erdmann"
6 | ],
7 | "license": "MIT",
8 | "ignore": [
9 | "**/.*",
10 | "node_modules",
11 | "bower_components",
12 | "test",
13 | "tests"
14 | ]
15 | }
16 |
--------------------------------------------------------------------------------
/gulpfile.js:
--------------------------------------------------------------------------------
1 | var gulp = require('gulp');
2 | var rename = require('gulp-rename');
3 | var babel = require('gulp-babel');
4 | var uglify = require('gulp-uglify');
5 |
6 | gulp.task('es6', function () {
7 | return gulp.src('*.es6.js')
8 | .pipe(babel({
9 | presets: ['es2015']
10 | }))
11 | .pipe(uglify())
12 | .pipe(rename(function (obj) {
13 | obj.basename = obj.basename.split('.')[0] + '.min';
14 | }))
15 | .pipe(gulp.dest('./'));
16 | });
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "vue-dragula",
3 | "version": "0.0.0",
4 | "description": "a vue.js plugin for dragula.js",
5 | "author": "Marc Erdmann",
6 | "license": "MIT",
7 | "devDependencies": {
8 | "autoprefixer": "^6.4.0",
9 | "babel-preset-es2015": "^6.13.2",
10 | "babel-preset-es2016": "^6.11.3",
11 | "cssnano": "^3.7.4",
12 | "gulp": "^3.9.1",
13 | "gulp-babel": "^6.1.2",
14 | "gulp-postcss": "^6.1.1",
15 | "gulp-rename": "^1.2.2",
16 | "gulp-sass": "^2.3.2",
17 | "gulp-uglify": "^2.0.0"
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/vue-dragula.es6.js:
--------------------------------------------------------------------------------
1 | //----- drag and drop -----//
2 | var vueDragula = {
3 | install: function (Vue, options) {
4 | 'use strict';
5 |
6 | Vue.dnd = {};
7 | Vue.dnd.dragIndex = 0;
8 |
9 | //----- Initialize drag and drop -----//
10 | Vue.dnd.initialize = function (elem, options) {
11 | options = options || {};
12 |
13 | var name = Object.getPrototypeOf(elem).constructor.name;
14 | if(Vue.dnd[name]) {
15 | elem.drake = Vue.dnd[name];
16 | } else {
17 | Vue.dnd[name] = Vue.dnd.dragulaInstance(options.handle, options.direction ? options.direction : 'vertical');
18 | elem.drake = Vue.dnd[name];
19 | }
20 | elem.drake.containers.push(elem.$el);
21 | };
22 |
23 | //----- safely destroy drag and drop -----//
24 | Vue.dnd.destroy = function(elem) {
25 | let dndIndex = elem.drake.containers.indexOf(elem.$el);
26 | if (dndIndex >= 0) elem.drake.containers.splice(dndIndex, 1);
27 | if (elem.drake.containers.length === 0) elem.drake.destroy();
28 | };
29 |
30 | //----- retrieve DOM index -----//
31 | Vue.dnd.domIndexOf = function (child, parent) {
32 | return Array.prototype.indexOf.call(parent.children, child);
33 | };
34 |
35 | //----- Query Builder -----//
36 | Vue.dnd.buildQuery = function (ary) {
37 | var query = 'self.vm';
38 | for (let x of ary) {
39 | if (isNaN(x)) {
40 | query = query + '.' + x;
41 | } else {
42 | query = query + '[' + x + ']';
43 | }
44 | }
45 | return query;
46 | };
47 |
48 | //----- Create Dragula Instances -----//
49 | Vue.dnd.dragulaInstance = function (handleParam, directionParam) {
50 | var drake = dragula({
51 | accepts: function (el, target, source, sibling) {
52 | if (this.containers.includes(target) && this.containers.includes(source)) return true;
53 | return false;
54 | },
55 | moves: function (el, container, handle) {
56 | if (!handleParam) return true;
57 | if (handle.classList.contains(handleParam)) return true;
58 | if (handle.parentNode.classList.contains(handleParam)) return true;
59 | return false;
60 | },
61 | direction: directionParam
62 | });
63 | drake.on('drag', function(el, source) {
64 | Vue.dnd.dragIndex = Vue.dnd.domIndexOf(el, source);
65 | });
66 | drake.on('drop', function (dropElem, target, source) {
67 | if (!target) return;
68 | var targetPosition = target.id.split("-");
69 | var sourcePosition = source.id.split("-");
70 |
71 | //----- This does the magic -----//
72 | // 1. a query is built e.g. self.vm.lists[1].title
73 | // 2. the query is evaluated, returns array
74 | // 3. array is spliced where element is dragged from
75 | // and dragged element is deleted and returned
76 | // 4. returned element is inserted at index of dropped element
77 | eval(Vue.dnd.buildQuery(targetPosition)).splice(Vue.dnd.domIndexOf(dropElem, target), 0, eval(Vue.dnd.buildQuery(sourcePosition)).splice(Vue.dnd.dragIndex, 1)[0]);
78 |
79 | });
80 | return drake;
81 | };
82 | }
83 | };
84 |
85 | Vue.use(vueDragula);
--------------------------------------------------------------------------------
/vue-dragula.min.js:
--------------------------------------------------------------------------------
1 | "use strict";var vueDragula={install:function install(Vue,options){Vue.dnd={},Vue.dnd.dragIndex=0,Vue.dnd.initialize=function(e,n){n=n||{};var r=Object.getPrototypeOf(e).constructor.name;Vue.dnd[r]?e.drake=Vue.dnd[r]:(Vue.dnd[r]=Vue.dnd.dragulaInstance(n.handle,n.direction?n.direction:"vertical"),e.drake=Vue.dnd[r]),e.drake.containers.push(e.$el)},Vue.dnd.destroy=function(e){var n=e.drake.containers.indexOf(e.$el);n>=0&&e.drake.containers.splice(n,1),0===e.drake.containers.length&&e.drake.destroy()},Vue.dnd.domIndexOf=function(e,n){return Array.prototype.indexOf.call(n.children,e)},Vue.dnd.buildQuery=function(e){var n="self.vm",r=!0,d=!1,a=void 0;try{for(var t,i=e[Symbol.iterator]();!(r=(t=i.next()).done);r=!0){var o=t.value;n=isNaN(o)?n+"."+o:n+"["+o+"]"}}catch(e){d=!0,a=e}finally{try{!r&&i.return&&i.return()}finally{if(d)throw a}}return n},Vue.dnd.dragulaInstance=function(handleParam,directionParam){var drake=dragula({accepts:function(e,n,r,d){return!(!this.containers.includes(n)||!this.containers.includes(r))},moves:function(e,n,r){return!handleParam||(!!r.classList.contains(handleParam)||!!r.parentNode.classList.contains(handleParam))},direction:directionParam});return drake.on("drag",function(e,n){Vue.dnd.dragIndex=Vue.dnd.domIndexOf(e,n)}),drake.on("drop",function(dropElem,target,source){if(target){var targetPosition=target.id.split("-"),sourcePosition=source.id.split("-");eval(Vue.dnd.buildQuery(targetPosition)).splice(Vue.dnd.domIndexOf(dropElem,target),0,eval(Vue.dnd.buildQuery(sourcePosition)).splice(Vue.dnd.dragIndex,1)[0])}}),drake}}};Vue.use(vueDragula);
--------------------------------------------------------------------------------