ember-sortable 2.2.5 → 3.0.0
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/.github/ISSUE_TEMPLATE/bug_report.md +2 -2
- package/.github/workflows/github_ci.yml +15 -13
- package/.huskyrc +0 -1
- package/CHANGELOG.md +234 -104
- package/MIGRATION_GUIDE_MODIFIERS.md +20 -13
- package/MIGRATION_GUIDE_V2.md +20 -8
- package/README.md +83 -149
- package/V2_MIGRATION_RFC.md +188 -222
- package/addon/modifiers/sortable-group.js +59 -52
- package/addon/modifiers/sortable-handle.js +16 -18
- package/addon/modifiers/sortable-item.js +124 -84
- package/addon/services/ember-sortable.js +3 -6
- package/addon/system/scroll-parent.js +1 -1
- package/addon/utils/coordinate.js +1 -1
- package/addon/utils/css-calculation.js +2 -2
- package/addon/utils/defaults.js +4 -3
- package/addon/utils/keyboard.js +3 -12
- package/addon-test-support/helpers/drag.js +3 -11
- package/addon-test-support/helpers/index.js +1 -4
- package/addon-test-support/helpers/reorder.js +4 -6
- package/addon-test-support/utils/keyboard.js +3 -12
- package/addon-test-support/utils/offset.js +6 -6
- package/config/environment.js +2 -2
- package/index.js +0 -20
- package/package.json +32 -18
- package/addon/components/sortable-group.js +0 -726
- package/addon/components/sortable-handle.js +0 -11
- package/addon/components/sortable-item.js +0 -753
- package/addon/styles/components/sortable-group.css +0 -9
- package/addon/templates/components/sortable-group.hbs +0 -16
- package/addon/templates/components/sortable-handle.hbs +0 -1
- package/addon/templates/components/sortable-item.hbs +0 -2
- package/addon/utils/transitionend.js +0 -22
- package/app/components/sortable-group.js +0 -3
- package/app/components/sortable-handle.js +0 -4
- package/app/components/sortable-item.js +0 -3
- package/jsconfig.json +0 -1
- package/vendor/polyfills/closest.js +0 -21
|
@@ -1,5 +1,7 @@
|
|
|
1
|
+
/* eslint-disable ember/no-computed-properties-in-native-classes */
|
|
2
|
+
/* eslint-disable ember/no-incorrect-calls-with-inline-anonymous-functions */
|
|
1
3
|
import Modifier from 'ember-modifier';
|
|
2
|
-
import {action, computed, set} from '@ember/object';
|
|
4
|
+
import { action, computed, set } from '@ember/object';
|
|
3
5
|
import {
|
|
4
6
|
isDownArrowKey,
|
|
5
7
|
isEnterKey,
|
|
@@ -7,12 +9,12 @@ import {
|
|
|
7
9
|
isLeftArrowKey,
|
|
8
10
|
isRightArrowKey,
|
|
9
11
|
isSpaceKey,
|
|
10
|
-
isUpArrowKey
|
|
11
|
-
} from
|
|
12
|
-
import {ANNOUNCEMENT_ACTION_TYPES} from
|
|
13
|
-
import { defaultA11yAnnouncementConfig } from
|
|
14
|
-
import {
|
|
15
|
-
import {inject as service} from '@ember/service';
|
|
12
|
+
isUpArrowKey,
|
|
13
|
+
} from '../utils/keyboard';
|
|
14
|
+
import { ANNOUNCEMENT_ACTION_TYPES } from '../utils/constant';
|
|
15
|
+
import { defaultA11yAnnouncementConfig } from '../utils/defaults';
|
|
16
|
+
import { next, schedule, scheduleOnce, later } from '@ember/runloop';
|
|
17
|
+
import { inject as service } from '@ember/service';
|
|
16
18
|
|
|
17
19
|
const NO_MODEL = {};
|
|
18
20
|
|
|
@@ -37,7 +39,6 @@ const NO_MODEL = {};
|
|
|
37
39
|
* </ol>
|
|
38
40
|
*/
|
|
39
41
|
export default class SortableGroupModifier extends Modifier {
|
|
40
|
-
|
|
41
42
|
/** Primary keyboard utils */
|
|
42
43
|
// Tracks the currently selected item
|
|
43
44
|
_selectedItem = null;
|
|
@@ -54,6 +55,10 @@ export default class SortableGroupModifier extends Modifier {
|
|
|
54
55
|
isRetainingFocus = false;
|
|
55
56
|
/** End of keyboard utils */
|
|
56
57
|
|
|
58
|
+
get disabled() {
|
|
59
|
+
return this.args.named.disabled || false;
|
|
60
|
+
}
|
|
61
|
+
|
|
57
62
|
/** Start of a11y properties */
|
|
58
63
|
/**
|
|
59
64
|
* @property an object containing different classes for visual indicators
|
|
@@ -87,7 +92,7 @@ export default class SortableGroupModifier extends Modifier {
|
|
|
87
92
|
}
|
|
88
93
|
|
|
89
94
|
get itemVisualClass() {
|
|
90
|
-
return this.args.named.itemVisualClass ||
|
|
95
|
+
return this.args.named.itemVisualClass || 'is-activated';
|
|
91
96
|
}
|
|
92
97
|
|
|
93
98
|
get a11yItemName() {
|
|
@@ -136,7 +141,7 @@ export default class SortableGroupModifier extends Modifier {
|
|
|
136
141
|
|
|
137
142
|
this.isRetainingFocus = true;
|
|
138
143
|
|
|
139
|
-
|
|
144
|
+
scheduleOnce('render', () => {
|
|
140
145
|
this.element.focus();
|
|
141
146
|
this.isRetainingFocus = false;
|
|
142
147
|
});
|
|
@@ -175,7 +180,7 @@ export default class SortableGroupModifier extends Modifier {
|
|
|
175
180
|
|
|
176
181
|
// switch direction values to notify sortedItems to update, so it sorts by direction.
|
|
177
182
|
let value;
|
|
178
|
-
const dimension = direction ===
|
|
183
|
+
const dimension = direction === 'y' ? 'height' : 'width';
|
|
179
184
|
// DOWN or RIGHT
|
|
180
185
|
if (toIndex > fromIndex) {
|
|
181
186
|
value = item[direction];
|
|
@@ -202,13 +207,13 @@ export default class SortableGroupModifier extends Modifier {
|
|
|
202
207
|
const direction = this.direction;
|
|
203
208
|
const selectedItem = this._selectedItem;
|
|
204
209
|
|
|
205
|
-
if (direction ===
|
|
210
|
+
if (direction === 'y' && isDownArrowKey(event)) {
|
|
206
211
|
this.moveItem(selectedItem, 1);
|
|
207
|
-
} else if (direction ===
|
|
212
|
+
} else if (direction === 'y' && isUpArrowKey(event)) {
|
|
208
213
|
this.moveItem(selectedItem, -1);
|
|
209
|
-
} else if (direction ===
|
|
214
|
+
} else if (direction === 'x' && isLeftArrowKey(event)) {
|
|
210
215
|
this.moveItem(selectedItem, -1);
|
|
211
|
-
} else if (direction ===
|
|
216
|
+
} else if (direction === 'x' && isRightArrowKey(event)) {
|
|
212
217
|
this.moveItem(selectedItem, 1);
|
|
213
218
|
} else if (isEnterKey(event) || isSpaceKey(event)) {
|
|
214
219
|
// confirm will reset the selectedItem, so caching it here before we remove it.
|
|
@@ -217,7 +222,7 @@ export default class SortableGroupModifier extends Modifier {
|
|
|
217
222
|
this.confirmKeyboardSelection();
|
|
218
223
|
|
|
219
224
|
this.isRetainingFocus = true;
|
|
220
|
-
|
|
225
|
+
scheduleOnce('render', () => this._focusItem(itemElement));
|
|
221
226
|
} else if (isEscapeKey(event)) {
|
|
222
227
|
// cancel will reset the selectedItem, so caching it here before we remove it.
|
|
223
228
|
const _selectedItemElement = selectedItem.element;
|
|
@@ -225,11 +230,11 @@ export default class SortableGroupModifier extends Modifier {
|
|
|
225
230
|
this.cancelKeyboardSelection();
|
|
226
231
|
|
|
227
232
|
this.isRetainingFocus = true;
|
|
228
|
-
|
|
233
|
+
scheduleOnce('render', () => {
|
|
229
234
|
const moves = this.moves;
|
|
230
235
|
if (moves && moves.length > 0) {
|
|
231
236
|
const sortedItems = this.sortedItems;
|
|
232
|
-
const itemElement = sortedItems[moves[0].fromIndex].element
|
|
237
|
+
const itemElement = sortedItems[moves[0].fromIndex].element;
|
|
233
238
|
this._focusItem(itemElement);
|
|
234
239
|
} else {
|
|
235
240
|
this._focusItem(_selectedItemElement);
|
|
@@ -258,7 +263,7 @@ export default class SortableGroupModifier extends Modifier {
|
|
|
258
263
|
}
|
|
259
264
|
this._announceAction(ANNOUNCEMENT_ACTION_TYPES.MOVE, delta);
|
|
260
265
|
// Guarantees that the before the UI is fully rendered before we move again.
|
|
261
|
-
|
|
266
|
+
scheduleOnce('render', () => {
|
|
262
267
|
this._move(sortedIndex, newSortedIndex);
|
|
263
268
|
this._updateHandleVisualIndicators(item, true);
|
|
264
269
|
|
|
@@ -281,7 +286,7 @@ export default class SortableGroupModifier extends Modifier {
|
|
|
281
286
|
const moves = this.moves;
|
|
282
287
|
while (moves.length > 0) {
|
|
283
288
|
const move = moves.pop();
|
|
284
|
-
this._move(move[1], move[0])
|
|
289
|
+
this._move(move[1], move[0]);
|
|
285
290
|
}
|
|
286
291
|
this._tearDownA11yApplicationContainer();
|
|
287
292
|
this._updateItemVisualIndicators(_selectedItem, false);
|
|
@@ -302,7 +307,7 @@ export default class SortableGroupModifier extends Modifier {
|
|
|
302
307
|
this.moves = [];
|
|
303
308
|
this._disableKeyboardReorderMode();
|
|
304
309
|
this._tearDownA11yApplicationContainer();
|
|
305
|
-
set(_selectedItem,
|
|
310
|
+
set(_selectedItem, 'wasDropped', true);
|
|
306
311
|
this.commit();
|
|
307
312
|
this._updateItemVisualIndicators(_selectedItem, false);
|
|
308
313
|
this._updateHandleVisualIndicators(_selectedItem, false);
|
|
@@ -331,7 +336,7 @@ export default class SortableGroupModifier extends Modifier {
|
|
|
331
336
|
const config = {
|
|
332
337
|
a11yItemName,
|
|
333
338
|
index: index,
|
|
334
|
-
maxLength
|
|
339
|
+
maxLength: sortedItems.length,
|
|
335
340
|
direction: this.direction,
|
|
336
341
|
delta,
|
|
337
342
|
};
|
|
@@ -340,7 +345,7 @@ export default class SortableGroupModifier extends Modifier {
|
|
|
340
345
|
announcer.textContent = message;
|
|
341
346
|
|
|
342
347
|
// Reset the message after the message is announced.
|
|
343
|
-
|
|
348
|
+
later(() => {
|
|
344
349
|
announcer.textContent = '';
|
|
345
350
|
}, 1000);
|
|
346
351
|
}
|
|
@@ -359,7 +364,6 @@ export default class SortableGroupModifier extends Modifier {
|
|
|
359
364
|
* @param {Boolean} isActive to activate or deactivate the class.
|
|
360
365
|
*/
|
|
361
366
|
_updateItemVisualIndicators(item, isActive) {
|
|
362
|
-
|
|
363
367
|
const itemVisualClass = this.itemVisualClass;
|
|
364
368
|
|
|
365
369
|
if (!itemVisualClass || !item) {
|
|
@@ -393,7 +397,7 @@ export default class SortableGroupModifier extends Modifier {
|
|
|
393
397
|
const visualHandle = handle ? handle : item.element;
|
|
394
398
|
const visualKeys = direction === 'y' ? ['UP', 'DOWN'] : ['LEFT', 'RIGHT'];
|
|
395
399
|
|
|
396
|
-
visualKeys.forEach(visualKey => {
|
|
400
|
+
visualKeys.forEach((visualKey) => {
|
|
397
401
|
visualHandle.classList.remove(handleVisualClass[visualKey]);
|
|
398
402
|
});
|
|
399
403
|
|
|
@@ -443,7 +447,7 @@ export default class SortableGroupModifier extends Modifier {
|
|
|
443
447
|
* Sets up the group as an application and make it programmatically focusable.
|
|
444
448
|
*/
|
|
445
449
|
_setupA11yApplicationContainer() {
|
|
446
|
-
this.element.setAttribute(
|
|
450
|
+
this.element.setAttribute('role', 'application');
|
|
447
451
|
this.element.tabIndex = -1;
|
|
448
452
|
}
|
|
449
453
|
|
|
@@ -451,8 +455,8 @@ export default class SortableGroupModifier extends Modifier {
|
|
|
451
455
|
* Tears down the `role=application` container.
|
|
452
456
|
*/
|
|
453
457
|
_tearDownA11yApplicationContainer() {
|
|
454
|
-
this.element.removeAttribute(
|
|
455
|
-
this.element.removeAttribute(
|
|
458
|
+
this.element.removeAttribute('role');
|
|
459
|
+
this.element.removeAttribute('tabIndex');
|
|
456
460
|
}
|
|
457
461
|
|
|
458
462
|
_prepareKeyboardReorderMode() {
|
|
@@ -468,7 +472,7 @@ export default class SortableGroupModifier extends Modifier {
|
|
|
468
472
|
@default y
|
|
469
473
|
*/
|
|
470
474
|
get direction() {
|
|
471
|
-
return this.args.named.direction ||
|
|
475
|
+
return this.args.named.direction || 'y';
|
|
472
476
|
}
|
|
473
477
|
|
|
474
478
|
/**
|
|
@@ -495,7 +499,7 @@ export default class SortableGroupModifier extends Modifier {
|
|
|
495
499
|
* @returns {*|string}
|
|
496
500
|
*/
|
|
497
501
|
get groupName() {
|
|
498
|
-
return this.args.named.groupName ||
|
|
502
|
+
return this.args.named.groupName || '_EmberSortableGroup';
|
|
499
503
|
}
|
|
500
504
|
|
|
501
505
|
_groupDef = this.sortableService.fetchGroup(this.groupName);
|
|
@@ -526,7 +530,7 @@ export default class SortableGroupModifier extends Modifier {
|
|
|
526
530
|
@property firstItemPosition
|
|
527
531
|
@type Number
|
|
528
532
|
*/
|
|
529
|
-
@computed(
|
|
533
|
+
@computed('direction', 'sortedItems')
|
|
530
534
|
get firstItemPosition() {
|
|
531
535
|
const direction = this.direction;
|
|
532
536
|
const sortedItems = this.sortedItems;
|
|
@@ -548,7 +552,7 @@ export default class SortableGroupModifier extends Modifier {
|
|
|
548
552
|
* Enables keyboard navigation
|
|
549
553
|
*/
|
|
550
554
|
@action
|
|
551
|
-
activateKeyDown(selectedItem){
|
|
555
|
+
activateKeyDown(selectedItem) {
|
|
552
556
|
this._selectedItem = selectedItem;
|
|
553
557
|
this.isKeyDownEnabled = true;
|
|
554
558
|
}
|
|
@@ -612,7 +616,7 @@ export default class SortableGroupModifier extends Modifier {
|
|
|
612
616
|
position = this.firstItemPosition;
|
|
613
617
|
}
|
|
614
618
|
|
|
615
|
-
sortedItems.forEach(item => {
|
|
619
|
+
sortedItems.forEach((item) => {
|
|
616
620
|
let dimension;
|
|
617
621
|
let direction = this.direction;
|
|
618
622
|
|
|
@@ -642,8 +646,8 @@ export default class SortableGroupModifier extends Modifier {
|
|
|
642
646
|
@action
|
|
643
647
|
commit() {
|
|
644
648
|
const items = this.sortedItems;
|
|
645
|
-
const itemModels = items.map(item => item.model);
|
|
646
|
-
const draggedItem = items.find(item => item.wasDropped);
|
|
649
|
+
const itemModels = items.map((item) => item.model);
|
|
650
|
+
const draggedItem = items.find((item) => item.wasDropped);
|
|
647
651
|
let draggedModel;
|
|
648
652
|
|
|
649
653
|
if (draggedItem) {
|
|
@@ -671,17 +675,17 @@ export default class SortableGroupModifier extends Modifier {
|
|
|
671
675
|
|
|
672
676
|
delete this._firstItemPosition;
|
|
673
677
|
|
|
674
|
-
|
|
675
|
-
items.forEach(item => item.freeze());
|
|
678
|
+
schedule('render', () => {
|
|
679
|
+
items.forEach((item) => item.freeze());
|
|
676
680
|
});
|
|
677
681
|
|
|
678
|
-
|
|
679
|
-
items.forEach(item => item.reset());
|
|
682
|
+
schedule('afterRender', () => {
|
|
683
|
+
items.forEach((item) => item.reset());
|
|
680
684
|
});
|
|
681
685
|
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
items.forEach(item => item.thaw());
|
|
686
|
+
next(() => {
|
|
687
|
+
schedule('render', () => {
|
|
688
|
+
items.forEach((item) => item.thaw());
|
|
685
689
|
});
|
|
686
690
|
});
|
|
687
691
|
}
|
|
@@ -697,24 +701,28 @@ export default class SortableGroupModifier extends Modifier {
|
|
|
697
701
|
// end of API
|
|
698
702
|
|
|
699
703
|
addEventListener() {
|
|
700
|
-
this.element.addEventListener(
|
|
701
|
-
this.element.addEventListener(
|
|
704
|
+
this.element.addEventListener('keydown', this.keyDown);
|
|
705
|
+
this.element.addEventListener('focusout', this.focusOut);
|
|
702
706
|
}
|
|
703
707
|
|
|
704
708
|
removeEventListener() {
|
|
705
|
-
this.element.removeEventListener(
|
|
706
|
-
this.element.removeEventListener(
|
|
709
|
+
this.element.removeEventListener('keydown', this.keyDown);
|
|
710
|
+
this.element.removeEventListener('focusout', this.focusOut);
|
|
707
711
|
}
|
|
708
712
|
|
|
709
713
|
didReceiveArguments() {
|
|
710
|
-
|
|
714
|
+
this.removeEventListener();
|
|
711
715
|
|
|
712
|
-
|
|
713
|
-
|
|
716
|
+
if (this.disabled) {
|
|
717
|
+
return;
|
|
718
|
+
}
|
|
714
719
|
|
|
715
|
-
didInstall() {
|
|
716
720
|
this.addEventListener();
|
|
721
|
+
}
|
|
717
722
|
|
|
723
|
+
didUpdateArguments() {}
|
|
724
|
+
|
|
725
|
+
didInstall() {
|
|
718
726
|
this.announcer = this._createAnnouncer();
|
|
719
727
|
this.element.insertAdjacentElement('afterend', this.announcer);
|
|
720
728
|
|
|
@@ -723,12 +731,11 @@ export default class SortableGroupModifier extends Modifier {
|
|
|
723
731
|
|
|
724
732
|
willRemove() {
|
|
725
733
|
// todo cleanup the announcer
|
|
726
|
-
if(this.announcer.parentNode) {
|
|
734
|
+
if (this.announcer.parentNode) {
|
|
727
735
|
this.announcer.parentNode.removeChild(this.announcer);
|
|
728
736
|
}
|
|
729
737
|
this.removeEventListener();
|
|
730
738
|
|
|
731
739
|
this.sortableService.deregisterGroup(this.groupName, this);
|
|
732
740
|
}
|
|
733
|
-
|
|
734
741
|
}
|
|
@@ -1,26 +1,24 @@
|
|
|
1
1
|
import Modifier from 'ember-modifier';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
* Modifier to to mark the handle of an item. If this is not supplied the item will be the handle
|
|
5
|
-
*
|
|
6
|
-
* @module drag-drop/draggable-group
|
|
7
|
-
* @example*
|
|
8
|
-
* <ol {{sortable-group onChange=this.update a11yAnnouncementConfig=this.myA11yConfig}}>
|
|
9
|
-
* {{#each model.items as |item|}}
|
|
10
|
-
* <li {{sortable-item model=item}}>
|
|
11
|
-
* {{item.name}}
|
|
12
|
-
* <span class="handle" {{sortable-handle}}>↕</span>
|
|
13
|
-
* </li>
|
|
14
|
-
* {{/each}}
|
|
15
|
-
* </ol>
|
|
16
|
-
*/
|
|
4
|
+
* Modifier to to mark the handle of an item. If this is not supplied the item will be the handle
|
|
5
|
+
*
|
|
6
|
+
* @module drag-drop/draggable-group
|
|
7
|
+
* @example*
|
|
8
|
+
* <ol {{sortable-group onChange=this.update a11yAnnouncementConfig=this.myA11yConfig}}>
|
|
9
|
+
* {{#each model.items as |item|}}
|
|
10
|
+
* <li {{sortable-item model=item}}>
|
|
11
|
+
* {{item.name}}
|
|
12
|
+
* <span class="handle" {{sortable-handle}}>↕</span>
|
|
13
|
+
* </li>
|
|
14
|
+
* {{/each}}
|
|
15
|
+
* </ol>
|
|
16
|
+
*/
|
|
17
17
|
export default class SortableHandleModifier extends Modifier {
|
|
18
|
-
|
|
19
18
|
didInstall() {
|
|
20
19
|
// take the model and look up the registered element, the tell that element you are the handle
|
|
21
|
-
this.element.dataset.sortableHandle=true;
|
|
22
|
-
this.element.tabIndex =
|
|
23
|
-
this.element.setAttribute(
|
|
20
|
+
this.element.dataset.sortableHandle = true;
|
|
21
|
+
this.element.tabIndex = '0';
|
|
22
|
+
this.element.setAttribute('role', 'button');
|
|
24
23
|
}
|
|
25
|
-
|
|
26
24
|
}
|