├── .gitignore
├── LICENSE
├── README.md
├── bower.json
├── build
└── js
│ ├── ng-draggable-widgets.js
│ └── ng-draggable-widgets.min.js
├── demo
├── drag_groups
│ ├── demo.css
│ ├── demo.js
│ └── index.html
├── nested_divs
│ ├── demo.css
│ ├── demo.js
│ └── index.html
├── percentage_widths
│ ├── demo.css
│ ├── demo.js
│ └── index.html
└── simple_dragging
│ ├── demo.css
│ ├── demo.js
│ └── index.html
├── gulpfile.js
├── index.html
├── karma.conf.js
├── karma
├── hit_zones.js
├── ng-draggable-widgets.testable.js
├── placeholder.js
└── widget.js
├── package.json
└── source
├── demo
├── drag_groups
│ ├── demo.js
│ ├── demo.scss
│ └── index.jade
├── nested_divs
│ ├── demo.js
│ ├── demo.scss
│ └── index.jade
├── percentage_widths
│ ├── demo.js
│ ├── demo.scss
│ └── index.jade
└── simple_dragging
│ ├── demo.js
│ ├── demo.scss
│ └── index.jade
└── js
└── ng-draggable-widgets.js
/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 |
5 | # Runtime data
6 | pids
7 | *.pid
8 | *.seed
9 |
10 | # Directory for instrumented libs generated by jscoverage/JSCover
11 | lib-cov
12 |
13 | # Coverage directory used by tools like istanbul
14 | coverage
15 |
16 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
17 | .grunt
18 |
19 | # node-waf configuration
20 | .lock-wscript
21 |
22 | # Compiled binary addons (http://nodejs.org/api/addons.html)
23 | build/Release
24 |
25 | # Dependency directory
26 | # https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git
27 | node_modules
28 |
29 | # Bower components
30 | public/components/*
31 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2015 Nicholas Johnson
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.
22 |
23 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # ng draggable widgets
2 |
3 | Draggable widgets for Angular. No jQuery dependency.
4 |
5 | ## Demo
6 |
7 | * [Demo index](http://forwardadvance.github.io/ng-draggable-widgets/)
8 | * [Simple Widget Dragging](http://forwardadvance.github.io/ng-draggable-widgets/demo/simple_dragging/)
9 | * [Dragging between groups](http://forwardadvance.github.io/ng-draggable-widgets/demo/drag_groups/)
10 | * [Widths can also be specified as percentages](http://forwardadvance.github.io/ng-draggable-widgets/demo/percentage_widths/)
11 | * [Dragged widgets can include complex arbitrary HTML](http://forwardadvance.github.io/ng-draggable-widgets/demo/nested_divs/)
12 |
13 |
14 | ## Usage
15 |
16 | Each draggable widget gets an attribute of draggable-widget and must contain an element with a draggable-widget-handle attribute.
17 |
18 |
19 |
20 |
21 |
drag handle
22 | Widget content
23 |
24 |
25 |
26 |
27 | Dragging the handle sets position absolute on the element causing it to drop out of the layout flow. On completion the callback is executed.
28 |
29 | ## Drag-group
30 |
31 | If you want automatic updating you must specify at least one drag group that points to an array in scope.
32 |
33 | ## Callback
34 |
35 | Specify a callback function with the draggable-widget-callback attribute. The callback will receive a drag object that contains references to the old and new arrays (assuming you have dragged between groups, and the insertion point. See the demo app for examples.
36 |
37 | ## Classes
38 |
39 | * Elements gain a class of dragging while being dragged.
40 |
41 | ## The Placeholder
42 |
43 | A div with a class of placeholder will be inserted into the DOM at the drop point. This will have the correct width and height. You may wish to style this div to match your widgets.
44 |
45 | ## Browser compatibility
46 |
47 | IE9+ only please. Mobile support not guaranteed.
48 |
49 | ## Licence
50 |
51 | MIT license, use it as you see fit. I'm not going to sue you.
52 |
53 |
--------------------------------------------------------------------------------
/bower.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "ng-drag-to-order",
3 | "main": "ngDragOrder.js",
4 | "version": "0.0.1",
5 | "homepage": "https://github.com/forwardadvance/ngDraggable",
6 | "ignore": [
7 | "**/.*",
8 | "node_modules",
9 | "bower_components",
10 | "test",
11 | "tests"
12 | ],
13 | "dependencies": {
14 | "angular": "latest",
15 | "angular-mocks": "latest"
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/build/js/ng-draggable-widgets.js:
--------------------------------------------------------------------------------
1 | // Nicholas Johnson (www.nicholasjohnson.com)
2 | // Forward Advance Training (www.forwardadvance.com)
3 | // MIT licence
4 |
5 | (function() {
6 | // Hitzones are positioned on the left and right of widgets
7 | // We call check to see if the current drag location is in a hitzone
8 | // If so we move the placeholder and update the drag destination.
9 | var hitZones = {
10 | size: 10,
11 | check: function(e) {
12 | var i, groups, widgets, widget, rect, width;
13 | groups = document.querySelectorAll('[drag-group]');
14 | for (var j = 0; j < groups.length; j++) {
15 | widgets = groups[j].querySelectorAll('[draggable-widget]:not(.dragging)');
16 | for (i = 0; i < widgets.length; i++) {
17 | widget = widgets[i];
18 | rect = widget.getBoundingClientRect();
19 | width = widget.offsetWidth;
20 | if ((e.pageY > rect.top) &&
21 | (e.pageY < rect.bottom) &&
22 | (e.pageX > rect.left) &&
23 | (e.pageX < rect.right - (width / 2))) {
24 | placeholder.insertBefore(widget);
25 | console.log(i);
26 | return {
27 | group: angular.element(widget).scope().dragGroup,
28 | index: i
29 | };
30 | }
31 | if ((e.pageY > rect.top) &&
32 | (e.pageY < rect.bottom) &&
33 | (e.pageX > rect.left + (width / 2)) &&
34 | (e.pageX < rect.right)) {
35 | placeholder.insertAfter(widget);
36 | console.log(i+1);
37 | return {
38 | group: angular.element(widget).scope().dragGroup,
39 | index: i+1
40 | };
41 | }
42 | }
43 | }
44 | }
45 | };
46 |
47 | var widget = {
48 | initDrag: function(el) {
49 | el.addClass('dragging');
50 | el.css({
51 | position:'absolute',
52 | 'z-index':100000
53 | });
54 | },
55 | setPosition: function(el, x, y, offsetX, offsetY) {
56 | el.css({
57 | left: x - offsetX - 20 + 'px',
58 | top: y - offsetY - 20 + 'px'
59 | });
60 | },
61 | stopDrag: function(el) {
62 | el.removeClass('dragging');
63 | el.css({
64 | position:'',
65 | left: '',
66 | top: '',
67 | 'z-index': ''
68 | });
69 | },
70 | lockSize: function(el) {
71 | el.css({
72 | width: el[0].offsetWidth + 'px',
73 | height: el[0].offsetHeight + 'px'
74 | });
75 | },
76 | unlockSize: function(el) {
77 | el.css({
78 | width: '',
79 | height: ''
80 | });
81 | }
82 | };
83 |
84 | // The placeholder object is a little rectangle with the same dimensions as the dragged widget
85 | // It gives the user a preview of page layout.
86 | // We can show or hide it, and insert it before or after another widget.
87 | var placeholder = {
88 | el: angular.element([
89 | '',
90 | '
'
91 | ].join('')),
92 | show: function(el) {
93 | placeholder.el[0].hidden = false;
94 | placeholder.el.css({
95 | width: el[0].clientWidth +'px',
96 | height: el[0].clientHeight +'px'
97 | });
98 | el.after(placeholder.el);
99 | },
100 | hide: function() {
101 | placeholder.el[0].hidden = true;
102 | },
103 | insertBefore: function(el) {
104 | el.parentElement.insertBefore(placeholder.el[0], el);
105 | },
106 | insertAfter: function(el) {
107 | angular.element(el).after(placeholder.el);
108 | }
109 | };
110 |
111 | // controller for the drag directive.
112 | // initialises the drag
113 | var dragController = /*@ngInject*/["$scope", function($scope) {
114 | var drag = $scope.drag = {};
115 | drag.start = function(e) {
116 | drag.initDrag(e);
117 | };
118 | drag.end = function() {
119 | drag.destroyDrag();
120 | };
121 | }];
122 | dragController.$inject = ["$scope"];
123 |
124 | angular.module('ng-draggable-widgets', [])
125 | .directive('dragGroup', ["$parse", function($parse) {
126 | return {
127 | scope:true,
128 | restrict: 'A',
129 | link: {
130 | pre: function(scope, el, attrs) {
131 | scope.dragGroup = {};
132 | scope.dragGroup = $parse(attrs.dragGroup)(scope);
133 | }
134 | }
135 | };
136 | }])
137 |
138 | .directive('draggableWidget', ["$rootScope", function($rootScope) {
139 | return {
140 | scope:true,
141 | restrict: 'A',
142 | controller: dragController,
143 | link: {
144 | pre: function(scope, el, attrs) {
145 | var drag = scope.drag;
146 |
147 | drag.callback = attrs.draggableWidgetCallback;
148 |
149 | // Set up the DOM for dragging
150 | drag.initDrag = function(e) {
151 | var dest;
152 | console.log('start drag');
153 | drag.offsetX = e.offsetX;
154 | drag.offsetY = e.offsetY;
155 | widget.lockSize(el);
156 | widget.initDrag(el);
157 | widget.setPosition(el, e.pageX, e.pageY, scope.drag.offsetX, scope.drag.offsetY);
158 | placeholder.show(el);
159 | scope.drag.source = {
160 | group: scope.dragGroup,
161 | index: scope.$index
162 | };
163 | angular.element(window).on('mousemove', function(e) {
164 | widget.setPosition(el, e.pageX, e.pageY, scope.drag.offsetX, scope.drag.offsetY);
165 | dest = hitZones.check(e);
166 | if (dest) {
167 | scope.drag.dest = dest;
168 | console.log('dest', dest);
169 | }
170 | });
171 | };
172 |
173 | // Unset the DOM for dragging
174 | scope.drag.destroyDrag = function() {
175 | var obj;
176 | console.log('stop drag');
177 | angular.element(window).off('mousemove');
178 | widget.stopDrag(el);
179 | widget.unlockSize(el);
180 | placeholder.hide();
181 | if (scope.drag.dest) {
182 | obj = scope.drag.source.group[scope.drag.source.index];
183 | scope.drag.source.group.splice(scope.drag.source.index, 1);
184 | scope.drag.dest.group.splice(scope.drag.dest.index, 0, obj);
185 | }
186 | if (drag.callback) {
187 | scope[drag.callback](scope.drag);
188 | }
189 | $rootScope.$apply();
190 | };
191 | }
192 | }
193 | };
194 | }])
195 |
196 | .directive('draggableWidgetHandle', function() {
197 | return {
198 | scope:true,
199 | restrict: 'A',
200 | link: {
201 | pre: function(scope, el) {
202 | el.on('mousedown', function(e) {
203 | scope.drag.initDrag(e);
204 | angular.element(document).one('mouseup', function(e) {
205 | scope.drag.destroyDrag(e);
206 | });
207 | });
208 | }
209 | }
210 | };
211 | });
212 |
213 | })();
--------------------------------------------------------------------------------
/build/js/ng-draggable-widgets.min.js:
--------------------------------------------------------------------------------
1 | // Nicholas Johnson (www.nicholasjohnson.com)
2 | // Forward Advance Training (www.forwardadvance.com)
3 | // MIT licence
4 |
5 | !function(){var e={size:10,check:function(e){var t,r,n,i,a,g;r=document.querySelectorAll("[drag-group]");for(var d=0;da.top&&e.pageYa.left&&e.pageXa.top&&e.pageYa.left+g/2&&e.pageX',""].join("")),show:function(e){o.el[0].hidden=!1,o.el.css({width:e[0].clientWidth+"px",height:e[0].clientHeight+"px"}),e.after(o.el)},hide:function(){o.el[0].hidden=!0},insertBefore:function(e){e.parentElement.insertBefore(o.el[0],e)},insertAfter:function(e){angular.element(e).after(o.el)}},r=["$scope",function(e){var t=e.drag={};t.start=function(e){t.initDrag(e)},t.end=function(){t.destroyDrag()}}];r.$inject=["$scope"],angular.module("ng-draggable-widgets",[]).directive("dragGroup",["$parse",function(e){return{scope:!0,restrict:"A",link:{pre:function(t,o,r){t.dragGroup={},t.dragGroup=e(r.dragGroup)(t)}}}}]).directive("draggableWidget",["$rootScope",function(n){return{scope:!0,restrict:"A",controller:r,link:{pre:function(r,i,a){var g=r.drag;g.callback=a.draggableWidgetCallback,g.initDrag=function(n){var a;console.log("start drag"),g.offsetX=n.offsetX,g.offsetY=n.offsetY,t.lockSize(i),t.initDrag(i),t.setPosition(i,n.pageX,n.pageY,r.drag.offsetX,r.drag.offsetY),o.show(i),r.drag.source={group:r.dragGroup,index:r.$index},angular.element(window).on("mousemove",function(o){t.setPosition(i,o.pageX,o.pageY,r.drag.offsetX,r.drag.offsetY),a=e.check(o),a&&(r.drag.dest=a,console.log("dest",a))})},r.drag.destroyDrag=function(){var e;console.log("stop drag"),angular.element(window).off("mousemove"),t.stopDrag(i),t.unlockSize(i),o.hide(),r.drag.dest&&(e=r.drag.source.group[r.drag.source.index],r.drag.source.group.splice(r.drag.source.index,1),r.drag.dest.group.splice(r.drag.dest.index,0,e)),g.callback&&r[g.callback](r.drag),n.$apply()}}}}}]).directive("draggableWidgetHandle",function(){return{scope:!0,restrict:"A",link:{pre:function(e,t){t.on("mousedown",function(t){e.drag.initDrag(t),angular.element(document).one("mouseup",function(t){e.drag.destroyDrag(t)})})}}}})}();
--------------------------------------------------------------------------------
/demo/drag_groups/demo.css:
--------------------------------------------------------------------------------
1 | html {
2 | box-sizing: border-box; }
3 |
4 | *, *:before, *:after {
5 | box-sizing: inherit; }
6 |
7 | body {
8 | width: 800px;
9 | margin: 0 auto;
10 | font-family: sans-serif;
11 | color: #fff;
12 | font-size: 100%; }
13 |
14 | .group-one, .group-two {
15 | width: 380px;
16 | background: grey;
17 | margin: 10px;
18 | padding: 10px; }
19 |
20 | .group-one {
21 | float: left; }
22 |
23 | .group-two {
24 | float: right; }
25 |
26 | div[draggable-widget] {
27 | width: 100px;
28 | height: 200px;
29 | border-radius: 5px;
30 | padding: 10px;
31 | float: left;
32 | margin: 10px; }
33 | div[draggable-widget].woks {
34 | background: aqua; }
35 | div[draggable-widget].mocks {
36 | background: cadetblue; }
37 | div[draggable-widget].socks {
38 | background: red;
39 | width: 220px; }
40 | div[draggable-widget].pops {
41 | background: darkseagreen; }
42 | div[draggable-widget].hocks {
43 | background: darkred; }
44 |
45 | div.widget-placeholder {
46 | border: 1px solid red;
47 | float: left;
48 | height: 200px;
49 | margin: 10px; }
50 |
51 | div[draggable-widget-handle] {
52 | border: 3px solid white;
53 | padding: 10px;
54 | background: rgba(0, 0, 0, 0.3);
55 | border-radius: 3px;
56 | color: white;
57 | margin-bottom: 1em;
58 | cursor: move; }
59 |
--------------------------------------------------------------------------------
/demo/drag_groups/demo.js:
--------------------------------------------------------------------------------
1 | (function() {
2 |
3 | var widgetGroups = [
4 | [
5 | {
6 | title:'Cats with Woks',
7 | class: 'woks'
8 | },
9 | {
10 | title:'Socks on Sticks',
11 | class: 'socks'
12 | },
13 | {
14 | title:'Mocks of Macs',
15 | class: 'mocks'
16 | },
17 | {
18 | title:'Pops in Pumps',
19 | class: 'pops'
20 | },
21 | {
22 | title:'Hocks of Rumps',
23 | class: 'hocks'
24 | }
25 | ],
26 | [
27 | {
28 | title:'Hocks of Rumps',
29 | class: 'hocks'
30 | },
31 | {
32 | title:'Cats with Woks',
33 | class: 'woks'
34 | },
35 | {
36 | title:'Pops in Pumps',
37 | class: 'pops'
38 | }
39 | ]
40 | ];
41 |
42 | angular.module('app', ['ng-draggable-widgets'])
43 | .controller('dragController', function($scope) {
44 | $scope.widgetGroups = widgetGroups;
45 | $scope.moveWidget = function(drag) {
46 | console.log(drag);
47 | }
48 | });
49 | })();
50 |
--------------------------------------------------------------------------------
/demo/drag_groups/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Widget dragging demo
5 |
6 |
7 |
8 |
9 |
10 |
11 |
17 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/demo/nested_divs/demo.css:
--------------------------------------------------------------------------------
1 | html {
2 | box-sizing: border-box; }
3 |
4 | *, *:before, *:after {
5 | box-sizing: inherit; }
6 |
7 | body {
8 | font-family: sans-serif;
9 | color: #fff;
10 | font-size: 100%;
11 | width: 900px;
12 | margin: 0 auto; }
13 |
14 | div[draggable-widget] {
15 | width: 200px;
16 | height: 300px;
17 | border-radius: 5px;
18 | padding: 10px;
19 | float: left;
20 | margin: 10px; }
21 | div[draggable-widget].woks {
22 | background: aqua; }
23 | div[draggable-widget].mocks {
24 | background: cadetblue; }
25 | div[draggable-widget].socks {
26 | background: red;
27 | width: 420px; }
28 | div[draggable-widget].pops {
29 | background: darkseagreen; }
30 | div[draggable-widget].hocks {
31 | background: darkred; }
32 |
33 | div.widget-placeholder {
34 | border: 1px solid red;
35 | float: left;
36 | height: 300px;
37 | margin: 10px; }
38 |
39 | div[draggable-widget-handle] {
40 | border: 3px solid white;
41 | padding: 10px;
42 | background: rgba(0, 0, 0, 0.3);
43 | border-radius: 3px;
44 | color: white;
45 | margin-bottom: 1em;
46 | cursor: move; }
47 |
--------------------------------------------------------------------------------
/demo/nested_divs/demo.js:
--------------------------------------------------------------------------------
1 | (function() {
2 |
3 | var widgets = [
4 | {
5 | title:'Cats with Woks',
6 | class: 'woks'
7 | },
8 | {
9 | title:'Socks on Sticks',
10 | class: 'socks'
11 | },
12 | {
13 | title:'Mocks of Macs',
14 | class: 'mocks'
15 | },
16 | {
17 | title:'Pops in Pumps',
18 | class: 'pops'
19 | },
20 | {
21 | title:'Hocks of Rumps',
22 | class: 'hocks'
23 | }
24 | ];
25 |
26 | angular.module('app', ['ng-draggable-widgets'])
27 | .controller('dragController', function($scope) {
28 | $scope.widgets = widgets;
29 | $scope.moveWidget = function(drag) {
30 | console.log(drag);
31 | }
32 | });
33 | })();
34 |
--------------------------------------------------------------------------------
/demo/nested_divs/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Widget dragging demo
5 |
6 |
7 |
8 |
9 |
10 |
11 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/demo/percentage_widths/demo.css:
--------------------------------------------------------------------------------
1 | html {
2 | box-sizing: border-box; }
3 |
4 | *, *:before, *:after {
5 | box-sizing: inherit; }
6 |
7 | body {
8 | font-family: sans-serif;
9 | color: #fff;
10 | font-size: 100%;
11 | width: 900px;
12 | margin: 0 auto; }
13 |
14 | div[draggable-widget] {
15 | width: 33.333%;
16 | height: 300px;
17 | padding: 10px;
18 | float: left; }
19 | div[draggable-widget].woks {
20 | background: aqua; }
21 | div[draggable-widget].mocks {
22 | background: cadetblue; }
23 | div[draggable-widget].socks {
24 | background: red;
25 | width: 66.6666%; }
26 | div[draggable-widget].pops {
27 | background: darkseagreen; }
28 | div[draggable-widget].hocks {
29 | background: darkred; }
30 |
31 | div.widget-placeholder {
32 | background: rgba(0, 0, 0, 0.2);
33 | float: left;
34 | height: 300px; }
35 |
36 | div[draggable-widget-handle] {
37 | padding: 10px;
38 | background: rgba(0, 0, 0, 0.3);
39 | border-radius: 3px;
40 | color: white;
41 | margin-bottom: 1em;
42 | cursor: move; }
43 |
--------------------------------------------------------------------------------
/demo/percentage_widths/demo.js:
--------------------------------------------------------------------------------
1 | (function() {
2 |
3 | var widgets = [
4 | {
5 | title:'Cats with Woks',
6 | class: 'woks'
7 | },
8 | {
9 | title:'Socks on Sticks',
10 | class: 'socks'
11 | },
12 | {
13 | title:'Mocks of Macs',
14 | class: 'mocks'
15 | },
16 | {
17 | title:'Pops in Pumps',
18 | class: 'pops'
19 | },
20 | {
21 | title:'Hocks of Rumps',
22 | class: 'hocks'
23 | }
24 | ];
25 |
26 | angular.module('app', ['ng-draggable-widgets'])
27 | .controller('dragController', function($scope) {
28 | $scope.widgets = widgets;
29 | $scope.moveWidget = function(drag) {
30 | console.log(drag);
31 | }
32 | });
33 | })();
34 |
--------------------------------------------------------------------------------
/demo/percentage_widths/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Widget dragging demo
5 |
6 |
7 |
8 |
9 |
10 |
11 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/demo/simple_dragging/demo.css:
--------------------------------------------------------------------------------
1 | html {
2 | box-sizing: border-box; }
3 |
4 | *, *:before, *:after {
5 | box-sizing: inherit; }
6 |
7 | body {
8 | font-family: sans-serif;
9 | color: #fff;
10 | font-size: 100%;
11 | width: 900px;
12 | margin: 0 auto; }
13 |
14 | div[draggable-widget] {
15 | width: 200px;
16 | height: 300px;
17 | border-radius: 5px;
18 | padding: 10px;
19 | float: left;
20 | margin: 10px; }
21 | div[draggable-widget].woks {
22 | background: aqua; }
23 | div[draggable-widget].mocks {
24 | background: cadetblue; }
25 | div[draggable-widget].socks {
26 | background: red;
27 | width: 420px; }
28 | div[draggable-widget].pops {
29 | background: darkseagreen; }
30 | div[draggable-widget].hocks {
31 | background: darkred; }
32 |
33 | div.widget-placeholder {
34 | border: 1px solid red;
35 | float: left;
36 | height: 300px;
37 | margin: 10px; }
38 |
39 | div[draggable-widget-handle] {
40 | border: 3px solid white;
41 | padding: 10px;
42 | background: rgba(0, 0, 0, 0.3);
43 | border-radius: 3px;
44 | color: white;
45 | margin-bottom: 1em;
46 | cursor: move; }
47 |
--------------------------------------------------------------------------------
/demo/simple_dragging/demo.js:
--------------------------------------------------------------------------------
1 | (function() {
2 |
3 | var widgets = [
4 | {
5 | title:'Cats with Woks',
6 | class: 'woks'
7 | },
8 | {
9 | title:'Socks on Sticks',
10 | class: 'socks'
11 | },
12 | {
13 | title:'Mocks of Macs',
14 | class: 'mocks'
15 | },
16 | {
17 | title:'Pops in Pumps',
18 | class: 'pops'
19 | },
20 | {
21 | title:'Hocks of Rumps',
22 | class: 'hocks'
23 | }
24 | ];
25 |
26 | angular.module('app', ['ng-draggable-widgets'])
27 | .controller('dragController', function($scope) {
28 | $scope.widgets = widgets;
29 | $scope.moveWidget = function(drag) {
30 | console.log(drag);
31 | }
32 | });
33 | })();
34 |
--------------------------------------------------------------------------------
/demo/simple_dragging/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Widget dragging demo
5 |
6 |
7 |
8 |
9 |
10 |
11 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/gulpfile.js:
--------------------------------------------------------------------------------
1 | var gulp = require('gulp'),
2 | livereload = require('gulp-livereload'),
3 | jshint = require('gulp-jshint'),
4 | concat = require('gulp-concat'),
5 | uglify = require('gulp-uglify'),
6 | rename = require('gulp-rename'),
7 | gutil = require('gulp-util'),
8 | ngAnnotate = require('gulp-ng-annotate'),
9 | beep = require('beepbeep'),
10 | header = require('gulp-header'),
11 | wrap = require("gulp-wrap"),
12 | sass = require('gulp-sass'),
13 | jade = require('gulp-jade'),
14 | minifycss = require('gulp-minify-css'),
15 | autoprefixer = require('gulp-autoprefixer'),
16 | plumber = require('gulp-plumber');
17 | karma = require('karma').server,
18 | package = require('./package.json');
19 |
20 | var dirs = {
21 | js: {
22 | src: './source/js',
23 | dest: './build/js'
24 | },
25 | demo: {
26 | src: './source/demo',
27 | dest: './demo'
28 | },
29 | test: {
30 | src: './source/js',
31 | dest: './karma'
32 | }
33 | };
34 |
35 | var jsHeader = [
36 | '// Nicholas Johnson (www.nicholasjohnson.com)',
37 | '// Forward Advance Training (www.forwardadvance.com)',
38 | '// MIT licence'
39 | ].join('\n');
40 |
41 | gulp.task('js', function () {
42 | return gulp.src([dirs.js.src, '*.js'].join('/'))
43 | .pipe(wrap('(function() {\n<%= contents %>\n})();'))
44 | .pipe(jshint())
45 | .pipe(jshint.reporter('default'))
46 | .on('error', beep)
47 | .pipe(concat(package.name + '.js'))
48 | .pipe(ngAnnotate())
49 | .on('error', gutil.noop)
50 | .pipe(header(jsHeader + '\n\n'))
51 | .pipe(gulp.dest(dirs.js.dest))
52 | .pipe(rename(package.name + '.min.js'))
53 | .pipe(uglify())
54 | .on('error', gutil.noop)
55 | .pipe(header(jsHeader + '\n\n'))
56 | .pipe(gulp.dest(dirs.js.dest))
57 | .pipe(livereload());
58 | });
59 |
60 | gulp.task('testJs', function () {
61 | return gulp.src([dirs.test.src, '*.js'].join('/'))
62 | .pipe(wrap(
63 | [
64 | '(function() {',
65 | '<%= contents %>',
66 | '// Smuggle locals out of closure for testing',
67 | 'window.placeholder = placeholder;',
68 | 'window.hitZones = hitZones;',
69 | 'window.widget = widget;',
70 | '})();'
71 | ].join('\n')
72 | ))
73 | .pipe(concat(package.name + '.testable.js'))
74 | .pipe(ngAnnotate())
75 | .on('error', gutil.noop)
76 | .pipe(header(jsHeader + '\n\n'))
77 | .pipe(gulp.dest(dirs.test.dest));
78 | });
79 |
80 | gulp.task('test', function (done) {
81 | karma.start({
82 | configFile: __dirname + '/karma.conf.js',
83 | singleRun: true
84 | }, done);
85 | });
86 |
87 | gulp.task('tdd', function (done) {
88 | karma.start({
89 | configFile: __dirname + '/karma.conf.js'
90 | }, done);
91 | });
92 |
93 | gulp.task('demo:jade', function() {
94 | return gulp.src([dirs.demo.src, '**', '*.jade'].join('/'))
95 | .pipe(jade({basedir: __dirname, pretty:true}))
96 | .on('error', gutil.log)
97 | .pipe(gulp.dest(dirs.demo.dest));
98 | })
99 |
100 | gulp.task('demo:js', function() {
101 | return gulp.src([dirs.demo.src, '**', '*.js'].join('/'))
102 | .pipe(gulp.dest(dirs.demo.dest));
103 | })
104 |
105 | gulp.task('demo:sass', function() {
106 | return gulp.src([dirs.demo.src, '**', '*.scss'].join('/'))
107 | .pipe(sass({ style: 'expanded' }))
108 | .pipe(autoprefixer('last 2 version', 'safari 5', 'ie 8', 'ie 9', 'opera 12.1'))
109 | .on('error', gutil.log)
110 | .pipe(gulp.dest(dirs.demo.dest));
111 | })
112 |
113 | gulp.task('demo', ['demo:jade', 'demo:js', 'demo:sass']);
114 |
115 | gulp.task('watch', function() {
116 | gulp.watch([dirs.js.src, '**', '*.js'].join('/'), ['js', 'testJs']);
117 | gulp.watch([dirs.demo.src, '**', '*'].join('/'), ['demo']);
118 | });
119 |
120 | gulp.task('default', [
121 | 'js',
122 | 'tdd',
123 | 'watch'
124 | ]);
125 |
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | ng-draggable-widgets-demo
5 |
10 |
11 |
12 | NG-Draggable Widgets
13 | A flexible drag and drop module for Angular
14 |
28 |
29 |
30 | Source code: https://github.com/forwardadvance/ng-draggable-widgets
31 |
32 |
33 |
34 |
35 |
--------------------------------------------------------------------------------
/karma.conf.js:
--------------------------------------------------------------------------------
1 | // Karma configuration
2 | // Generated on Thu Apr 23 2015 15:53:13 GMT+0100 (BST)
3 |
4 | module.exports = function(config) {
5 | config.set({
6 |
7 | // base path that will be used to resolve all patterns (eg. files, exclude)
8 | basePath: '',
9 |
10 |
11 | // frameworks to use
12 | // available frameworks: https://npmjs.org/browse/keyword/karma-adapter
13 | frameworks: ['jasmine'],
14 |
15 |
16 | // list of files / patterns to load in the browser
17 | files: [
18 | 'public/components/angular/angular.js',
19 | 'public/components/angular-mocks/angular-mocks.js',
20 | 'karma/ng-draggable-widgets.testable.js',
21 | 'karma/*.js'
22 | ],
23 |
24 |
25 | // list of files to exclude
26 | exclude: [
27 | ],
28 |
29 |
30 | // preprocess matching files before serving them to the browser
31 | // available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor
32 | preprocessors: {
33 | },
34 |
35 |
36 | // test results reporter to use
37 | // possible values: 'dots', 'progress'
38 | // available reporters: https://npmjs.org/browse/keyword/karma-reporter
39 | reporters: ['progress'],
40 |
41 |
42 | // web server port
43 | port: 9876,
44 |
45 |
46 | // enable / disable colors in the output (reporters and logs)
47 | colors: true,
48 |
49 |
50 | // level of logging
51 | // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
52 | logLevel: config.LOG_INFO,
53 |
54 |
55 | // enable / disable watching file and executing tests whenever any file changes
56 | autoWatch: true,
57 |
58 |
59 | // start these browsers
60 | // available browser launchers: https://npmjs.org/browse/keyword/karma-launcher
61 | browsers: ['Chrome'],
62 |
63 |
64 | // Continuous Integration mode
65 | // if true, Karma captures browsers, runs the tests and exits
66 | singleRun: false
67 | });
68 | };
69 |
--------------------------------------------------------------------------------
/karma/hit_zones.js:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nicholas-johnson/ng-draggable-widgets/4c5df9fb7146609696a28b77fb1f46861864e952/karma/hit_zones.js
--------------------------------------------------------------------------------
/karma/ng-draggable-widgets.testable.js:
--------------------------------------------------------------------------------
1 | // Nicholas Johnson (www.nicholasjohnson.com)
2 | // Forward Advance Training (www.forwardadvance.com)
3 | // MIT licence
4 |
5 | (function() {
6 | // Hitzones are positioned on the left and right of widgets
7 | // We call check to see if the current drag location is in a hitzone
8 | // If so we move the placeholder and update the drag destination.
9 | var hitZones = {
10 | size: 10,
11 | check: function(e) {
12 | var i, groups, widgets, widget, rect, width;
13 | groups = document.querySelectorAll('[drag-group]');
14 | for (var j = 0; j < groups.length; j++) {
15 | widgets = groups[j].querySelectorAll('[draggable-widget]:not(.dragging)');
16 | for (i = 0; i < widgets.length; i++) {
17 | widget = widgets[i];
18 | rect = widget.getBoundingClientRect();
19 | width = widget.offsetWidth;
20 | if ((e.pageY > rect.top) &&
21 | (e.pageY < rect.bottom) &&
22 | (e.pageX > rect.left) &&
23 | (e.pageX < rect.right - (width / 2))) {
24 | placeholder.insertBefore(widget);
25 | console.log(i);
26 | return {
27 | group: angular.element(widget).scope().dragGroup,
28 | index: i
29 | };
30 | }
31 | if ((e.pageY > rect.top) &&
32 | (e.pageY < rect.bottom) &&
33 | (e.pageX > rect.left + (width / 2)) &&
34 | (e.pageX < rect.right)) {
35 | placeholder.insertAfter(widget);
36 | console.log(i+1);
37 | return {
38 | group: angular.element(widget).scope().dragGroup,
39 | index: i+1
40 | };
41 | }
42 | }
43 | }
44 | }
45 | };
46 |
47 | var widget = {
48 | initDrag: function(el) {
49 | el.addClass('dragging');
50 | el.css({
51 | position:'absolute',
52 | 'z-index':100000
53 | });
54 | },
55 | setPosition: function(el, x, y, offsetX, offsetY) {
56 | el.css({
57 | left: x - offsetX - 20 + 'px',
58 | top: y - offsetY - 20 + 'px'
59 | });
60 | },
61 | stopDrag: function(el) {
62 | el.removeClass('dragging');
63 | el.css({
64 | position:'',
65 | left: '',
66 | top: '',
67 | 'z-index': ''
68 | });
69 | },
70 | lockSize: function(el) {
71 | el.css({
72 | width: el[0].offsetWidth + 'px',
73 | height: el[0].offsetHeight + 'px'
74 | });
75 | },
76 | unlockSize: function(el) {
77 | el.css({
78 | width: '',
79 | height: ''
80 | });
81 | }
82 | };
83 |
84 | // The placeholder object is a little rectangle with the same dimensions as the dragged widget
85 | // It gives the user a preview of page layout.
86 | // We can show or hide it, and insert it before or after another widget.
87 | var placeholder = {
88 | el: angular.element([
89 | '',
90 | '
'
91 | ].join('')),
92 | show: function(el) {
93 | placeholder.el[0].hidden = false;
94 | placeholder.el.css({
95 | width: el[0].clientWidth +'px',
96 | height: el[0].clientHeight +'px'
97 | });
98 | el.after(placeholder.el);
99 | },
100 | hide: function() {
101 | placeholder.el[0].hidden = true;
102 | },
103 | insertBefore: function(el) {
104 | el.parentElement.insertBefore(placeholder.el[0], el);
105 | },
106 | insertAfter: function(el) {
107 | angular.element(el).after(placeholder.el);
108 | }
109 | };
110 |
111 | // controller for the drag directive.
112 | // initialises the drag
113 | var dragController = /*@ngInject*/["$scope", function($scope) {
114 | var drag = $scope.drag = {};
115 | drag.start = function(e) {
116 | drag.initDrag(e);
117 | };
118 | drag.end = function() {
119 | drag.destroyDrag();
120 | };
121 | }];
122 | dragController.$inject = ["$scope"];
123 |
124 | angular.module('ng-draggable-widgets', [])
125 | .directive('dragGroup', ["$parse", function($parse) {
126 | return {
127 | scope:true,
128 | restrict: 'A',
129 | link: {
130 | pre: function(scope, el, attrs) {
131 | scope.dragGroup = {};
132 | scope.dragGroup = $parse(attrs.dragGroup)(scope);
133 | }
134 | }
135 | };
136 | }])
137 |
138 | .directive('draggableWidget', ["$rootScope", function($rootScope) {
139 | return {
140 | scope:true,
141 | restrict: 'A',
142 | controller: dragController,
143 | link: {
144 | pre: function(scope, el, attrs) {
145 | var drag = scope.drag;
146 |
147 | drag.callback = attrs.draggableWidgetCallback;
148 |
149 | // Set up the DOM for dragging
150 | drag.initDrag = function(e) {
151 | var dest;
152 | console.log('start drag');
153 | drag.offsetX = e.offsetX;
154 | drag.offsetY = e.offsetY;
155 | widget.lockSize(el);
156 | widget.initDrag(el);
157 | widget.setPosition(el, e.pageX, e.pageY, scope.drag.offsetX, scope.drag.offsetY);
158 | placeholder.show(el);
159 | scope.drag.source = {
160 | group: scope.dragGroup,
161 | index: scope.$index
162 | };
163 | angular.element(window).on('mousemove', function(e) {
164 | widget.setPosition(el, e.pageX, e.pageY, scope.drag.offsetX, scope.drag.offsetY);
165 | dest = hitZones.check(e);
166 | if (dest) {
167 | scope.drag.dest = dest;
168 | console.log('dest', dest);
169 | }
170 | });
171 | };
172 |
173 | // Unset the DOM for dragging
174 | scope.drag.destroyDrag = function() {
175 | var obj;
176 | console.log('stop drag');
177 | angular.element(window).off('mousemove');
178 | widget.stopDrag(el);
179 | widget.unlockSize(el);
180 | placeholder.hide();
181 | if (scope.drag.dest) {
182 | obj = scope.drag.source.group[scope.drag.source.index];
183 | scope.drag.source.group.splice(scope.drag.source.index, 1);
184 | scope.drag.dest.group.splice(scope.drag.dest.index, 0, obj);
185 | }
186 | if (drag.callback) {
187 | scope[drag.callback](scope.drag);
188 | }
189 | $rootScope.$apply();
190 | };
191 | }
192 | }
193 | };
194 | }])
195 |
196 | .directive('draggableWidgetHandle', function() {
197 | return {
198 | scope:true,
199 | restrict: 'A',
200 | link: {
201 | pre: function(scope, el) {
202 | el.on('mousedown', function(e) {
203 | scope.drag.initDrag(e);
204 | angular.element(document).one('mouseup', function(e) {
205 | scope.drag.destroyDrag(e);
206 | });
207 | });
208 | }
209 | }
210 | };
211 | });
212 |
213 | // Smuggle locals out of closure for testing
214 | window.placeholder = placeholder;
215 | window.hitZones = hitZones;
216 | window.widget = widget;
217 | })();
--------------------------------------------------------------------------------
/karma/placeholder.js:
--------------------------------------------------------------------------------
1 | describe('ng-draggable-widgets', function() {
2 |
3 | var scope, el, widgetEl;
4 |
5 | beforeEach(module('ng-draggable-widgets'));
6 |
7 | beforeEach(inject(function($rootScope, $compile) {
8 | el = angular.element([
9 | '',
10 | '
',
11 | '
',
12 | '
'
13 | ].join(''));
14 | scope = $rootScope.$new();
15 | widgetEl = angular.element(el[0].getElementsByClassName('widget')[0]);
16 | $compile(el)(scope);
17 | scope.$digest();
18 | }));
19 |
20 | it('has an element', function() {
21 | expect(placeholder.el).toBeDefined();
22 | });
23 |
24 | it('can be shown', function() {
25 | console.log(widgetEl);
26 | placeholder.show(widgetEl);
27 | expect(placeholder.el[0].hidden).toBe(false);
28 | expect(placeholder.el[0].style.width).toBe('0px');
29 | expect(placeholder.el[0].style.width).toBe('0px');
30 | });
31 |
32 | it('can be hidden', function() {
33 | placeholder.hide();
34 | expect(placeholder.el[0].hidden).toBe(true);
35 | });
36 |
37 | });
38 |
--------------------------------------------------------------------------------
/karma/widget.js:
--------------------------------------------------------------------------------
1 | describe('ng-draggable-widgets', function() {
2 |
3 | var scope, el;
4 |
5 | beforeEach(module('ng-draggable-widgets'));
6 |
7 | beforeEach(inject(function($rootScope, $compile) {
8 | el = angular.element([
9 | '',
10 | '
'
11 | ].join(''));
12 | scope = $rootScope.$new();
13 | $compile(el)(scope);
14 | scope.$digest();
15 | }));
16 |
17 | it('can be initialised', function() {
18 | widget.initDrag(el);
19 | expect(el[0].style.position).toBe('absolute');
20 | });
21 |
22 | it('can be positioned', function() {
23 | widget.setPosition(el, 100, 100, 20, 20);
24 | expect(el[0].style.left).toBe('60px');
25 | expect(el[0].style.top).toBe('60px');
26 | });
27 |
28 | it('can be unintialised', function() {
29 | widget.stopDrag(el);
30 | expect(el[0].style.position).toBe('');
31 | expect(el[0].style.left).toBe('');
32 | expect(el[0].style.top).toBe('');
33 | });
34 |
35 | it('can have a size locked', function() {
36 | widget.lockSize(el);
37 | expect(el[0].style.width).toBe('0px');
38 | expect(el[0].style.height).toBe('0px');
39 | });
40 |
41 | it('can have a size unlocked', function() {
42 | widget.unlockSize(el);
43 | expect(el[0].style.width).toBe('');
44 | expect(el[0].style.height).toBe('');
45 | })
46 |
47 | });
48 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "ng-draggable-widgets",
3 | "version": "0.0.1",
4 | "description": "Drag widgets to reorder",
5 | "main": "index.js",
6 | "directories": {
7 | "doc": "doc",
8 | "test": "test"
9 | },
10 | "scripts": {
11 | "test": "echo \"Error: no test specified\" && exit 1"
12 | },
13 | "repository": {
14 | "type": "git",
15 | "url": "ssh://git@github.com/forwardadvance/drag-to-sort.git"
16 | },
17 | "author": "Nicholas Johnson",
18 | "license": "MIT",
19 | "bugs": {
20 | "url": "https://github.com/forwardadvance/ng-draggable-widgets/issues"
21 | },
22 | "homepage": "https://github.com/forwardadvance/ng-draggable-widgets",
23 | "devDependencies": {
24 | "beepbeep": "^1.2.0",
25 | "bower": "^1.4.1",
26 | "coffee-script": "^1.8.0",
27 | "gulp": "^3.8.11",
28 | "gulp-autoprefixer": "^2.1.0",
29 | "gulp-concat": "^2.4.3",
30 | "gulp-header": "^1.2.2",
31 | "gulp-jade": "^1.0.0",
32 | "gulp-jshint": "^1.9.2",
33 | "gulp-livereload": "^3.7.0",
34 | "gulp-minify-css": "^0.4.5",
35 | "gulp-ng-annotate": "^0.5.2",
36 | "gulp-plumber": "^1.0.0",
37 | "gulp-rename": "^1.2.0",
38 | "gulp-sass": "^2.1.1",
39 | "gulp-uglify": "^1.1.0",
40 | "gulp-util": "^3.0.3",
41 | "gulp-wrap": "^0.11.0",
42 | "jasmine-core": "^2.2.0",
43 | "karma": "^0.12.31",
44 | "karma-chrome-launcher": "^0.1.8",
45 | "karma-jasmine": "^0.3.5"
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/source/demo/drag_groups/demo.js:
--------------------------------------------------------------------------------
1 | (function() {
2 |
3 | var widgetGroups = [
4 | [
5 | {
6 | title:'Cats with Woks',
7 | class: 'woks'
8 | },
9 | {
10 | title:'Socks on Sticks',
11 | class: 'socks'
12 | },
13 | {
14 | title:'Mocks of Macs',
15 | class: 'mocks'
16 | },
17 | {
18 | title:'Pops in Pumps',
19 | class: 'pops'
20 | },
21 | {
22 | title:'Hocks of Rumps',
23 | class: 'hocks'
24 | }
25 | ],
26 | [
27 | {
28 | title:'Hocks of Rumps',
29 | class: 'hocks'
30 | },
31 | {
32 | title:'Cats with Woks',
33 | class: 'woks'
34 | },
35 | {
36 | title:'Pops in Pumps',
37 | class: 'pops'
38 | }
39 | ]
40 | ];
41 |
42 | angular.module('app', ['ng-draggable-widgets'])
43 | .controller('dragController', function($scope) {
44 | $scope.widgetGroups = widgetGroups;
45 | $scope.moveWidget = function(drag) {
46 | console.log(drag);
47 | }
48 | });
49 | })();
50 |
--------------------------------------------------------------------------------
/source/demo/drag_groups/demo.scss:
--------------------------------------------------------------------------------
1 | $width: 100px;
2 | $height: 200px;
3 | $margin: 10px;
4 |
5 | html {
6 | box-sizing: border-box;
7 | }
8 |
9 | *, *:before, *:after {
10 | box-sizing: inherit;
11 | }
12 |
13 | body {
14 | width: $width * 8;
15 | margin:0 auto;
16 | font-family:sans-serif;
17 | color:#fff;
18 | font-size: 100%;
19 | }
20 |
21 | .group-one, .group-two {
22 | width:3*$width + 8*$margin;
23 | background:grey;
24 | margin:$margin;
25 | padding:$margin;
26 | }
27 |
28 | .group-one {
29 | float:left;
30 | }
31 |
32 | .group-two {
33 | float:right;
34 | }
35 |
36 | div[draggable-widget] {
37 | width:$width;
38 | height:$height;
39 | border-radius:5px;
40 | padding:10px;
41 | float:left;
42 | margin:$margin;
43 | &.woks {
44 | background:aqua;
45 | }
46 | &.mocks {
47 | background:cadetblue;
48 | }
49 | &.socks {
50 | background:red;
51 | width:2 * $width + $margin*2;
52 | }
53 | &.pops {
54 | background:darkseagreen;
55 | }
56 | &.hocks {
57 | background:darkred;
58 | }
59 | }
60 | div.widget-placeholder {
61 | border:1px solid red;
62 | float:left;
63 | height:$height;
64 | margin:$margin;
65 | }
66 | div[draggable-widget-handle] {
67 | border:3px solid rgba(255,255,255,1);
68 | padding:10px;
69 | background:rgba(0,0,0,0.3);
70 | border-radius:3px;
71 | color:white;
72 | margin-bottom:1em;
73 | cursor: move;
74 | }
75 |
--------------------------------------------------------------------------------
/source/demo/drag_groups/index.jade:
--------------------------------------------------------------------------------
1 | doctype html
2 | html( ng-app='app' )
3 | head
4 | title Widget dragging demo
5 | script( src='../../public/components/angular/angular.js' )
6 | script( src='../../build/js/ng-draggable-widgets.js' )
7 | script( src='./demo.js' )
8 | link( rel="stylesheet" type='text/css' href='./demo.css' )
9 | body( ng-controller='dragController' )
10 | div.group-one( drag-group='widgetGroups[0]' )
11 | div( ng-repeat='widget in widgetGroups[0]' draggable-widget='widget' draggable-widget-callback='moveWidget' class='{{widget.class}}' )
12 | div( draggable-widget-handle ) drag
13 | p {{widget.title}}
14 |
15 | div.group-two( drag-group='widgetGroups[1]' )
16 | div( ng-repeat='widget in widgetGroups[1]' draggable-widget='widget' draggable-widget-callback='moveWidget' class='{{widget.class}}' )
17 | div( draggable-widget-handle ) drag
18 | p {{widget.title}}
19 |
--------------------------------------------------------------------------------
/source/demo/nested_divs/demo.js:
--------------------------------------------------------------------------------
1 | (function() {
2 |
3 | var widgets = [
4 | {
5 | title:'Cats with Woks',
6 | class: 'woks'
7 | },
8 | {
9 | title:'Socks on Sticks',
10 | class: 'socks'
11 | },
12 | {
13 | title:'Mocks of Macs',
14 | class: 'mocks'
15 | },
16 | {
17 | title:'Pops in Pumps',
18 | class: 'pops'
19 | },
20 | {
21 | title:'Hocks of Rumps',
22 | class: 'hocks'
23 | }
24 | ];
25 |
26 | angular.module('app', ['ng-draggable-widgets'])
27 | .controller('dragController', function($scope) {
28 | $scope.widgets = widgets;
29 | $scope.moveWidget = function(drag) {
30 | console.log(drag);
31 | }
32 | });
33 | })();
34 |
--------------------------------------------------------------------------------
/source/demo/nested_divs/demo.scss:
--------------------------------------------------------------------------------
1 | $width: 200px;
2 | $height: 300px;
3 | $margin: 10px;
4 |
5 | html {
6 | box-sizing: border-box;
7 | }
8 | *, *:before, *:after {
9 | box-sizing: inherit;
10 | }
11 |
12 | body {
13 | font-family:sans-serif;
14 | color:#fff;
15 | font-size: 100%;
16 | width:4*$width + 10*$margin;
17 | margin:0 auto;
18 | }
19 |
20 | div[draggable-widget] {
21 | width:$width;
22 | height:$height;
23 | border-radius:5px;
24 | padding:10px;
25 | float:left;
26 | margin:$margin;
27 | &.woks {
28 | background:aqua;
29 | }
30 | &.mocks {
31 | background:cadetblue;
32 | }
33 | &.socks {
34 | background:red;
35 | width:2 * $width + $margin*2;
36 | }
37 | &.pops {
38 | background:darkseagreen;
39 | }
40 | &.hocks {
41 | background:darkred;
42 | }
43 | }
44 | div.widget-placeholder {
45 | border:1px solid red;
46 | float:left;
47 | height:$height;
48 | margin:$margin;
49 | }
50 | div[draggable-widget-handle] {
51 | border:3px solid rgba(255,255,255,1);
52 | padding:10px;
53 | background:rgba(0,0,0,0.3);
54 | border-radius:3px;
55 | color:white;
56 | margin-bottom:1em;
57 | cursor: move;
58 | }
59 |
--------------------------------------------------------------------------------
/source/demo/nested_divs/index.jade:
--------------------------------------------------------------------------------
1 | doctype html
2 | html( ng-app='app' )
3 | head
4 | title Widget dragging demo
5 | script( src='../../public/components/angular/angular.js' )
6 | script( src='../../build/js/ng-draggable-widgets.js' )
7 | script( src='./demo.js' )
8 | link( rel="stylesheet" type='text/css' href='./demo.css' )
9 | body( ng-controller='dragController' )
10 | div( drag-group='widgets' )
11 | div
12 | div
13 | div( ng-repeat='widget in widgets' draggable-widget='widget' draggable-widget-callback='moveWidget' class='{{widget.class}}' )
14 | div
15 | div
16 | div( draggable-widget-handle ) drag
17 | p {{widget.title}}
18 |
--------------------------------------------------------------------------------
/source/demo/percentage_widths/demo.js:
--------------------------------------------------------------------------------
1 | (function() {
2 |
3 | var widgets = [
4 | {
5 | title:'Cats with Woks',
6 | class: 'woks'
7 | },
8 | {
9 | title:'Socks on Sticks',
10 | class: 'socks'
11 | },
12 | {
13 | title:'Mocks of Macs',
14 | class: 'mocks'
15 | },
16 | {
17 | title:'Pops in Pumps',
18 | class: 'pops'
19 | },
20 | {
21 | title:'Hocks of Rumps',
22 | class: 'hocks'
23 | }
24 | ];
25 |
26 | angular.module('app', ['ng-draggable-widgets'])
27 | .controller('dragController', function($scope) {
28 | $scope.widgets = widgets;
29 | $scope.moveWidget = function(drag) {
30 | console.log(drag);
31 | }
32 | });
33 | })();
34 |
--------------------------------------------------------------------------------
/source/demo/percentage_widths/demo.scss:
--------------------------------------------------------------------------------
1 | $width: 200px;
2 | $height: 300px;
3 | $margin: 10px;
4 |
5 | html {
6 | box-sizing: border-box;
7 | }
8 | *, *:before, *:after {
9 | box-sizing: inherit;
10 | }
11 |
12 | body {
13 | font-family:sans-serif;
14 | color:#fff;
15 | font-size: 100%;
16 | width:4*$width + 10*$margin;
17 | margin:0 auto;
18 | }
19 |
20 | div[draggable-widget] {
21 | width:33.333%;
22 | height:$height;
23 | padding:10px;
24 | float:left;
25 | &.woks {
26 | background:aqua;
27 | }
28 | &.mocks {
29 | background:cadetblue;
30 | }
31 | &.socks {
32 | background:red;
33 | width:66.6666%;
34 | }
35 | &.pops {
36 | background:darkseagreen;
37 | }
38 | &.hocks {
39 | background:darkred;
40 | }
41 | }
42 | div.widget-placeholder {
43 | background:rgba(0,0,0,0.2);
44 | float:left;
45 | height:$height;
46 | }
47 | div[draggable-widget-handle] {
48 | padding:10px;
49 | background:rgba(0,0,0,0.3);
50 | border-radius:3px;
51 | color:white;
52 | margin-bottom:1em;
53 | cursor: move;
54 | }
55 |
--------------------------------------------------------------------------------
/source/demo/percentage_widths/index.jade:
--------------------------------------------------------------------------------
1 | doctype html
2 | html( ng-app='app' )
3 | head
4 | title Widget dragging demo
5 | script( src='../../public/components/angular/angular.js' )
6 | script( src='../../build/js/ng-draggable-widgets.js' )
7 | script( src='./demo.js' )
8 | link( rel="stylesheet" type='text/css' href='./demo.css' )
9 | body( ng-controller='dragController' )
10 | div( drag-group='widgets' )
11 | div( ng-repeat='widget in widgets' draggable-widget='widget' draggable-widget-callback='moveWidget' class='{{widget.class}}' )
12 | div( draggable-widget-handle ) drag
13 | p {{widget.title}}
14 |
--------------------------------------------------------------------------------
/source/demo/simple_dragging/demo.js:
--------------------------------------------------------------------------------
1 | (function() {
2 |
3 | var widgets = [
4 | {
5 | title:'Cats with Woks',
6 | class: 'woks'
7 | },
8 | {
9 | title:'Socks on Sticks',
10 | class: 'socks'
11 | },
12 | {
13 | title:'Mocks of Macs',
14 | class: 'mocks'
15 | },
16 | {
17 | title:'Pops in Pumps',
18 | class: 'pops'
19 | },
20 | {
21 | title:'Hocks of Rumps',
22 | class: 'hocks'
23 | }
24 | ];
25 |
26 | angular.module('app', ['ng-draggable-widgets'])
27 | .controller('dragController', function($scope) {
28 | $scope.widgets = widgets;
29 | $scope.moveWidget = function(drag) {
30 | console.log(drag);
31 | }
32 | });
33 | })();
34 |
--------------------------------------------------------------------------------
/source/demo/simple_dragging/demo.scss:
--------------------------------------------------------------------------------
1 | $width: 200px;
2 | $height: 300px;
3 | $margin: 10px;
4 |
5 | html {
6 | box-sizing: border-box;
7 | }
8 | *, *:before, *:after {
9 | box-sizing: inherit;
10 | }
11 |
12 | body {
13 | font-family:sans-serif;
14 | color:#fff;
15 | font-size: 100%;
16 | width:4*$width + 10*$margin;
17 | margin:0 auto;
18 | }
19 |
20 | div[draggable-widget] {
21 | width:$width;
22 | height:$height;
23 | border-radius:5px;
24 | padding:10px;
25 | float:left;
26 | margin:$margin;
27 | &.woks {
28 | background:aqua;
29 | }
30 | &.mocks {
31 | background:cadetblue;
32 | }
33 | &.socks {
34 | background:red;
35 | width:2 * $width + $margin*2;
36 | }
37 | &.pops {
38 | background:darkseagreen;
39 | }
40 | &.hocks {
41 | background:darkred;
42 | }
43 | }
44 | div.widget-placeholder {
45 | border:1px solid red;
46 | float:left;
47 | height:$height;
48 | margin:$margin;
49 | }
50 | div[draggable-widget-handle] {
51 | border:3px solid rgba(255,255,255,1);
52 | padding:10px;
53 | background:rgba(0,0,0,0.3);
54 | border-radius:3px;
55 | color:white;
56 | margin-bottom:1em;
57 | cursor: move;
58 | }
59 |
--------------------------------------------------------------------------------
/source/demo/simple_dragging/index.jade:
--------------------------------------------------------------------------------
1 | doctype html
2 | html( ng-app='app' )
3 | head
4 | title Widget dragging demo
5 | script( src='../../public/components/angular/angular.js' )
6 | script( src='../../build/js/ng-draggable-widgets.js' )
7 | script( src='./demo.js' )
8 | link( rel="stylesheet" type='text/css' href='./demo.css' )
9 | body( ng-controller='dragController' )
10 | div( drag-group='widgets' )
11 | div( ng-repeat='widget in widgets' draggable-widget='widget' draggable-widget-callback='moveWidget' class='{{widget.class}}' )
12 | div( draggable-widget-handle ) drag
13 | p {{widget.title}}
14 |
--------------------------------------------------------------------------------
/source/js/ng-draggable-widgets.js:
--------------------------------------------------------------------------------
1 | // Hitzones are positioned on the left and right of widgets
2 | // We call check to see if the current drag location is in a hitzone
3 | // If so we move the placeholder and update the drag destination.
4 | var hitZones = {
5 | size: 10,
6 | check: function(e) {
7 | var i, groups, widgets, widget, rect, width;
8 | groups = document.querySelectorAll('[drag-group]');
9 | for (var j = 0; j < groups.length; j++) {
10 | widgets = groups[j].querySelectorAll('[draggable-widget]:not(.dragging)');
11 | for (i = 0; i < widgets.length; i++) {
12 | widget = widgets[i];
13 | rect = widget.getBoundingClientRect();
14 | width = widget.offsetWidth;
15 | if ((e.pageY > rect.top) &&
16 | (e.pageY < rect.bottom) &&
17 | (e.pageX > rect.left) &&
18 | (e.pageX < rect.right - (width / 2))) {
19 | placeholder.insertBefore(widget);
20 | console.log(i);
21 | return {
22 | group: angular.element(widget).scope().dragGroup,
23 | index: i
24 | };
25 | }
26 | if ((e.pageY > rect.top) &&
27 | (e.pageY < rect.bottom) &&
28 | (e.pageX > rect.left + (width / 2)) &&
29 | (e.pageX < rect.right)) {
30 | placeholder.insertAfter(widget);
31 | console.log(i+1);
32 | return {
33 | group: angular.element(widget).scope().dragGroup,
34 | index: i+1
35 | };
36 | }
37 | }
38 | }
39 | }
40 | };
41 |
42 | var widget = {
43 | initDrag: function(el) {
44 | el.addClass('dragging');
45 | el.css({
46 | position:'absolute',
47 | 'z-index':100000
48 | });
49 | },
50 | setPosition: function(el, x, y, offsetX, offsetY) {
51 | el.css({
52 | left: x - offsetX - 20 + 'px',
53 | top: y - offsetY - 20 + 'px'
54 | });
55 | },
56 | stopDrag: function(el) {
57 | el.removeClass('dragging');
58 | el.css({
59 | position:'',
60 | left: '',
61 | top: '',
62 | 'z-index': ''
63 | });
64 | },
65 | lockSize: function(el) {
66 | el.css({
67 | width: el[0].offsetWidth + 'px',
68 | height: el[0].offsetHeight + 'px'
69 | });
70 | },
71 | unlockSize: function(el) {
72 | el.css({
73 | width: '',
74 | height: ''
75 | });
76 | }
77 | };
78 |
79 | // The placeholder object is a little rectangle with the same dimensions as the dragged widget
80 | // It gives the user a preview of page layout.
81 | // We can show or hide it, and insert it before or after another widget.
82 | var placeholder = {
83 | el: angular.element([
84 | '',
85 | '
'
86 | ].join('')),
87 | show: function(el) {
88 | placeholder.el[0].hidden = false;
89 | placeholder.el.css({
90 | width: el[0].clientWidth +'px',
91 | height: el[0].clientHeight +'px'
92 | });
93 | el.after(placeholder.el);
94 | },
95 | hide: function() {
96 | placeholder.el[0].hidden = true;
97 | },
98 | insertBefore: function(el) {
99 | el.parentElement.insertBefore(placeholder.el[0], el);
100 | },
101 | insertAfter: function(el) {
102 | angular.element(el).after(placeholder.el);
103 | }
104 | };
105 |
106 | // controller for the drag directive.
107 | // initialises the drag
108 | var dragController = /*@ngInject*/function($scope) {
109 | var drag = $scope.drag = {};
110 | drag.start = function(e) {
111 | drag.initDrag(e);
112 | };
113 | drag.end = function() {
114 | drag.destroyDrag();
115 | };
116 | };
117 |
118 | angular.module('ng-draggable-widgets', [])
119 | .directive('dragGroup', function($parse) {
120 | return {
121 | scope:true,
122 | restrict: 'A',
123 | link: {
124 | pre: function(scope, el, attrs) {
125 | scope.dragGroup = {};
126 | scope.dragGroup = $parse(attrs.dragGroup)(scope);
127 | }
128 | }
129 | };
130 | })
131 |
132 | .directive('draggableWidget', function($rootScope) {
133 | return {
134 | scope:true,
135 | restrict: 'A',
136 | controller: dragController,
137 | link: {
138 | pre: function(scope, el, attrs) {
139 | var drag = scope.drag;
140 |
141 | drag.callback = attrs.draggableWidgetCallback;
142 |
143 | // Set up the DOM for dragging
144 | drag.initDrag = function(e) {
145 | var dest;
146 | console.log('start drag');
147 | drag.offsetX = e.offsetX;
148 | drag.offsetY = e.offsetY;
149 | widget.lockSize(el);
150 | widget.initDrag(el);
151 | widget.setPosition(el, e.pageX, e.pageY, scope.drag.offsetX, scope.drag.offsetY);
152 | placeholder.show(el);
153 | scope.drag.source = {
154 | group: scope.dragGroup,
155 | index: scope.$index
156 | };
157 | angular.element(window).on('mousemove', function(e) {
158 | widget.setPosition(el, e.pageX, e.pageY, scope.drag.offsetX, scope.drag.offsetY);
159 | dest = hitZones.check(e);
160 | if (dest) {
161 | scope.drag.dest = dest;
162 | console.log('dest', dest);
163 | }
164 | });
165 | };
166 |
167 | // Unset the DOM for dragging
168 | scope.drag.destroyDrag = function() {
169 | var obj;
170 | console.log('stop drag');
171 | angular.element(window).off('mousemove');
172 | widget.stopDrag(el);
173 | widget.unlockSize(el);
174 | placeholder.hide();
175 | if (scope.drag.dest) {
176 | obj = scope.drag.source.group[scope.drag.source.index];
177 | scope.drag.source.group.splice(scope.drag.source.index, 1);
178 | scope.drag.dest.group.splice(scope.drag.dest.index, 0, obj);
179 | }
180 | if (drag.callback) {
181 | scope[drag.callback](scope.drag);
182 | }
183 | $rootScope.$apply();
184 | };
185 | }
186 | }
187 | };
188 | })
189 |
190 | .directive('draggableWidgetHandle', function() {
191 | return {
192 | scope:true,
193 | restrict: 'A',
194 | link: {
195 | pre: function(scope, el) {
196 | el.on('mousedown', function(e) {
197 | scope.drag.initDrag(e);
198 | angular.element(document).one('mouseup', function(e) {
199 | scope.drag.destroyDrag(e);
200 | });
201 | });
202 | }
203 | }
204 | };
205 | });
206 |
--------------------------------------------------------------------------------