Skip to content

Commit c1073ae

Browse files
committed
[WIP] Managed list
1 parent 52b809c commit c1073ae

File tree

17 files changed

+3305
-384
lines changed

17 files changed

+3305
-384
lines changed

Diff for: addon/components/sortable-js.hbs

+10-8
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
1-
<div
2-
class="ember-sortable-js"
3-
{{did-insert this.didInsert}}
4-
{{did-update this.setOptions @options}}
5-
...attributes
6-
>
7-
{{yield this.sortable}}
8-
</div>
1+
{{#let (element this.element) as |Element|}}
2+
<Element
3+
class="ember-sortable-js"
4+
{{did-insert this.didInsert}}
5+
{{did-update this.setOptions @options}}
6+
...attributes
7+
>
8+
{{yield this.internalList}}
9+
</Element>
10+
{{/let}}

Diff for: addon/components/sortable-js.js

+83-13
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,47 @@
11
import Component from '@glimmer/component';
22
import Sortable from 'sortablejs';
3-
import { bind } from '@ember/runloop';
3+
import { bind, scheduleOnce } from '@ember/runloop';
44
import { action } from '@ember/object';
5+
import { next } from '@ember/runloop';
6+
import { tracked } from '@glimmer/tracking';
7+
import { move, insertAt, removeFrom } from 'ember-sortablejs/utils/array-utils';
8+
import { inject as service } from '@ember/service';
59

610
export default class SortableJsComponent extends Component {
7-
sortable = null;
11+
@service dragStore;
12+
@tracked list = [];
813

9-
#events = [
10-
'onChoose',
11-
'onUnchoose',
14+
#sortableContainer = null;
15+
#sortableInstance = null;
16+
#internalEvents = [
1217
'onStart',
13-
'onEnd',
1418
'onAdd',
1519
'onUpdate',
16-
'onSort',
1720
'onRemove',
21+
'onEnd',
22+
];
23+
#events = [
1824
'onMove',
19-
'onClone',
2025
'onChange',
26+
'onChoose',
27+
'onUnchoose',
28+
'onSort',
29+
'onClone',
2130
'scrollFn',
2231
'setData',
2332
'onFilter',
2433
'onSpill',
2534
];
2635

36+
get element() {
37+
return this.args.tag || 'div';
38+
}
39+
40+
get internalList() {
41+
// fix identity diffing
42+
return this.list.map((item, i) => ({ id: i += 1, item }))
43+
}
44+
2745
@action
2846
setOptions() {
2947
for (let [key, value] of Object.entries(this.args.options)) {
@@ -40,23 +58,75 @@ export default class SortableJsComponent extends Component {
4058
const defaults = {};
4159
const options = Object.assign({}, defaults, this.args.options);
4260

43-
this.sortable = Sortable.create(element, options);
44-
this.setupEventHandlers();
61+
this.#sortableContainer = element;
62+
63+
next(this, () => {
64+
this.#sortableInstance = Sortable.create(element, options);
65+
this.list = [...(this.args.items || [])];
66+
this.setupEventHandlers();
67+
this.setupInternalEventHandlers();
68+
});
4569
}
4670

4771
willDestroy() {
48-
this.sortable.destroy();
72+
this.#sortableInstance.destroy();
73+
}
74+
75+
onUpdate(evt) {
76+
const {
77+
oldDraggableIndex,
78+
newDraggableIndex,
79+
} = evt;
80+
81+
this.list = move(this.list, oldDraggableIndex, newDraggableIndex);
82+
this.args?.onUpdate?.(evt);
83+
}
84+
85+
onRemove(evt) {
86+
const {
87+
oldDraggableIndex,
88+
} = evt;
89+
90+
this.list = removeFrom(this.list, oldDraggableIndex);
91+
this.args?.onRemove?.(evt);
92+
}
93+
94+
onAdd(evt) {
95+
// evt.item.remove(); // without this DOM is wrong
96+
const {
97+
oldDraggableIndex,
98+
newDraggableIndex,
99+
} = evt;
100+
const oldItem = this.dragStore.dragging.list[oldDraggableIndex];
101+
this.list = insertAt(this.list, newDraggableIndex, oldItem)
102+
this.args?.onAdd?.(evt);
103+
}
104+
105+
onStart(evt) {
106+
this.dragStore.dragging = this;
107+
this.args?.onStart?.(evt);
108+
}
109+
110+
onEnd(evt) {
111+
this.args?.onEnd?.(evt);
112+
this.dragStore.dragging = null;
49113
}
50114

51115
setupEventHandlers() {
52116
this.#events.forEach(eventName => {
53117
const action = this.args[eventName];
54118
if (typeof action === 'function') {
55-
this.sortable.option(eventName, bind(this, 'performExternalAction', eventName));
119+
this.#sortableInstance.option(eventName, bind(this, 'performExternalAction', eventName));
56120
}
57121
});
58122
}
59123

124+
setupInternalEventHandlers() {
125+
this.#internalEvents.forEach(eventName => {
126+
this.#sortableInstance.option(eventName, bind(this, this[eventName]));
127+
});
128+
}
129+
60130
performExternalAction(actionName, ...args) {
61131
let action = this.args[actionName];
62132

@@ -66,6 +136,6 @@ export default class SortableJsComponent extends Component {
66136
}
67137

68138
setOption(option, value) {
69-
this.sortable.option(option, value);
139+
this.#sortableInstance.option(option, value);
70140
}
71141
}

Diff for: addon/services/drag-store.js

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import Service from '@ember/service';
2+
3+
export default class DragStoreService extends Service {
4+
dragging = null;
5+
}

Diff for: addon/utils/array-utils.js

+37
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
/**
2+
* Moves an array element from one index to another
3+
* @param {Array} arr
4+
* @param {Number} from
5+
* @param {Number} to
6+
* @returns {Array} [ ]
7+
*/
8+
export function move(arr, from, to) {
9+
const clone = [...arr];
10+
clone.splice(to, 0, clone.splice(from, 1)[0]);
11+
return clone;
12+
}
13+
14+
/**
15+
* Insert an item to an array in the specified index
16+
* @param {Array} arr
17+
* @param {Number} index
18+
* @param {any} item
19+
* @returns {Array} [ ]
20+
*/
21+
export function insertAt(arr, index, item) {
22+
const clone = [...arr];
23+
clone.splice(index, 0, item)
24+
return clone;
25+
}
26+
27+
/**
28+
* Insert an item to an array in the specified index
29+
* @param {Array} arr
30+
* @param {Number} index
31+
* @returns {Array} [ ]
32+
*/
33+
export function removeFrom(arr, index) {
34+
const clone = [...arr];
35+
clone.splice(index, 1);
36+
return clone;
37+
}

Diff for: app/services/drag-store.js

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export { default } from 'ember-sortablejs/services/drag-store';

0 commit comments

Comments
 (0)