]> git.proxmox.com Git - sencha-touch.git/blob - src/src/plugin/SortableList.js
import Sencha Touch 2.4.2 source
[sencha-touch.git] / src / src / plugin / SortableList.js
1 /**
2 * @class Ext.plugin.SortableList
3 * @extends Ext.Component
4 * The SortableList plugin gives your list items the ability to be reordered by tapping and
5 * dragging elements within the item.
6 *
7 * The list-sortablehandle is not added to your tpl by default, so it's important that you
8 * manually include it. It's also important to recognize that list-items are not draggable
9 * themselves. You must add an element to the itemTpl for it to be dragged.
10 *
11 * Ext.Viewport.add({
12 * xtype: 'list',
13 * infinite: true,
14 * plugins: 'sortablelist',
15 * itemTpl: '<span class="myStyle ' + Ext.baseCSSPrefix + 'list-sortablehandle"></span>{text}',
16 * data: [{
17 * text: 'Item 1'
18 * }, {
19 * text: 'Item 2'
20 * }, {
21 * text: 'Item 3'
22 * }]
23 * });
24 *
25 * The CSS for MyStyle can be anything that creates an element to tap and drag. For this
26 * example we made a simple rectangle like so:
27 *
28 * .myStyle{
29 * width:30px;
30 * height:20px;
31 * background:gray;
32 * float:left;
33 * }
34 *
35 * Note: You must have infinite set to 'true' when using the SortableList plugin.
36 *
37 */
38 Ext.define('Ext.plugin.SortableList', {
39 extend: 'Ext.Component',
40
41 alias: 'plugin.sortablelist',
42
43 mixins: ['Ext.mixin.Bindable'],
44
45 config: {
46 list: null,
47 handleSelector: '.' + Ext.baseCSSPrefix + 'list-sortablehandle'
48 },
49
50 init: function(list) {
51 this.setList(list);
52 },
53
54 updateList: function(list) {
55 if (list) {
56 if (list.initialized) {
57 this.attachListeners();
58 }
59 else {
60 list.on({
61 initialize: 'attachListeners',
62 scope: this,
63 single: true
64 });
65 }
66 }
67 },
68
69 attachListeners: function() {
70 var list = this.getList(),
71 scrollerElement = list.getScrollable().getScroller().getContainer();
72
73 this.scrollerElement = scrollerElement;
74
75 scrollerElement.onBefore({
76 dragstart: 'onScrollerDragStart',
77 scope: this
78 });
79 },
80
81 onScrollerDragStart: function(e, target) {
82 if (Ext.DomQuery.is(target, this.getHandleSelector())) {
83 if (!this.animating) {
84 this.onDragStart(e, target);
85 }
86 return false;
87 }
88 },
89
90 onDragStart: function(e) {
91 var row = Ext.getCmp(e.getTarget('.' + Ext.baseCSSPrefix + 'list-item').id),
92 list = this.getList(),
93 store = list.getStore();
94
95 this.scrollerElement.on({
96 drag: 'onDrag',
97 dragend: 'onDragEnd',
98 scope: this
99 });
100
101 this.positionMap = list.getItemMap();
102 this.listStore = store;
103 this.previousIndexDistance = 0;
104
105 this.dragRow = row;
106 this.dragRecord = row.getRecord();
107
108 this.dragRowIndex = this.currentDragRowIndex = row.$dataIndex;
109 this.dragRowHeight = this.positionMap.getItemHeight(this.dragRowIndex);
110
111 if (list.getInfinite()) {
112 this.startTranslate = this.positionMap.map[this.dragRowIndex];
113 } else {
114 row.translate(0, 0);
115 this.startTranslate = 0;
116 }
117
118 row.addCls(Ext.baseCSSPrefix + 'list-item-dragging');
119 },
120
121 onDrag: function(e) {
122 var list = this.getList(),
123 listItems = list.listItems,
124 collection = list.getStore().data,
125 dragRow = this.dragRow,
126 dragRecordKey = collection.getKey(dragRow.getRecord()),
127 listItemInfo = list.getListItemInfo(),
128 positionMap = this.positionMap,
129 distance = 0,
130 i, item, ln, targetItem, targetIndex, itemIndex,
131 swapIndex, swapPosition, record, swapKey, draggingUp;
132
133 this.dragRowPosition = this.startTranslate + e.deltaY;
134 dragRow.translate(0, this.dragRowPosition);
135
136 targetIndex = positionMap.findIndex(this.dragRowPosition + (this.dragRowHeight / 2));
137 targetItem = list.getItemAt(targetIndex);
138
139 if (targetItem) {
140 distance = targetIndex - this.currentDragRowIndex;
141
142 if (distance !== 0) {
143 draggingUp = (distance < 0);
144
145 for (i = 0, ln = Math.abs(distance); i < ln; i++) {
146 if (draggingUp) {
147 swapIndex = this.currentDragRowIndex - i;
148 item = list.getItemAt(swapIndex - 1);
149 } else {
150 swapIndex = this.currentDragRowIndex + i;
151 item = list.getItemAt(swapIndex + 1);
152 }
153
154 swapPosition = positionMap.map[swapIndex];
155
156 item.translate(0, swapPosition);
157
158 record = item.getRecord();
159 swapKey = collection.getKey(record);
160
161 Ext.Array.remove(collection.items, record);
162 Ext.Array.remove(collection.all, record);
163 collection.items.splice(swapIndex, 0, record);
164 collection.all.splice(swapIndex, 0, record);
165 collection.indices[dragRecordKey] = collection.indices[swapKey];
166 collection.indices[swapKey] = swapIndex;
167
168 list.updateListItem(item, swapIndex, listItemInfo);
169 item.$position = swapPosition;
170 }
171
172 itemIndex = listItems.indexOf(dragRow);
173 Ext.Array.remove(listItems, dragRow);
174 listItems.splice(itemIndex + distance, 0, dragRow);
175
176 dragRow.$dataIndex = targetIndex;
177 dragRow.$position = positionMap.map[targetIndex];
178
179 this.currentDragRowIndex = targetIndex;
180 }
181 }
182 },
183
184 onDragEnd: function() {
185 var me = this,
186 row = this.dragRow,
187 list = this.getList(),
188 listItemInfo = list.getListItemInfo(),
189 position = row.$position;
190
191 this.scrollerElement.un({
192 drag: 'onDrag',
193 dragend: 'onDragEnd',
194 scope: this
195 });
196
197 this.animating = true;
198
199 row.getTranslatable().on('animationend', function() {
200 row.removeCls(Ext.baseCSSPrefix + 'list-item-dragging');
201
202 list.updateListItem(row, row.$dataIndex, listItemInfo);
203 row.$position = position;
204
205 list.fireEvent('dragsort', list, row, this.currentDragRowIndex, this.dragRowIndex);
206 this.animating = false;
207 }, me, {single: true});
208
209 row.translate(0, position, {duration: 100});
210 }
211 });