ember-sortable 2.4.0 → 4.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/CHANGELOG.md +239 -108
- package/MIGRATION_GUIDE_MODIFIERS.md +20 -13
- package/MIGRATION_GUIDE_V2.md +20 -8
- package/README.md +94 -169
- package/RELEASE.md +60 -0
- package/V2_MIGRATION_RFC.md +188 -222
- package/addon/modifiers/sortable-group.js +69 -58
- package/addon/modifiers/sortable-handle.js +22 -19
- package/addon/modifiers/sortable-item.js +126 -82
- 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/app/services/ember-sortable-internal-state.js +1 -0
- package/config/environment.js +2 -2
- package/index.js +0 -20
- package/package.json +56 -33
- package/.github/ISSUE_TEMPLATE/bug_report.md +0 -25
- package/.github/workflows/github_ci.yml +0 -69
- package/addon/components/sortable-group.js +0 -741
- package/addon/components/sortable-handle.js +0 -30
- package/addon/components/sortable-item.js +0 -770
- 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/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,13 @@ import {
|
|
|
7
9
|
isLeftArrowKey,
|
|
8
10
|
isRightArrowKey,
|
|
9
11
|
isSpaceKey,
|
|
10
|
-
isUpArrowKey
|
|
11
|
-
} from
|
|
12
|
-
import {ANNOUNCEMENT_ACTION_TYPES} from
|
|
13
|
-
import { defaultA11yAnnouncementConfig } from
|
|
12
|
+
isUpArrowKey,
|
|
13
|
+
} from '../utils/keyboard';
|
|
14
|
+
import { ANNOUNCEMENT_ACTION_TYPES } from '../utils/constant';
|
|
15
|
+
import { defaultA11yAnnouncementConfig } from '../utils/defaults';
|
|
14
16
|
import { next, schedule, scheduleOnce, later } from '@ember/runloop';
|
|
15
|
-
import {inject as service} from '@ember/service';
|
|
17
|
+
import { inject as service } from '@ember/service';
|
|
18
|
+
import { registerDestructor, isDestroyed } from '@ember/destroyable';
|
|
16
19
|
|
|
17
20
|
const NO_MODEL = {};
|
|
18
21
|
|
|
@@ -37,7 +40,6 @@ const NO_MODEL = {};
|
|
|
37
40
|
* </ol>
|
|
38
41
|
*/
|
|
39
42
|
export default class SortableGroupModifier extends Modifier {
|
|
40
|
-
|
|
41
43
|
/** Primary keyboard utils */
|
|
42
44
|
// Tracks the currently selected item
|
|
43
45
|
_selectedItem = null;
|
|
@@ -91,7 +93,7 @@ export default class SortableGroupModifier extends Modifier {
|
|
|
91
93
|
}
|
|
92
94
|
|
|
93
95
|
get itemVisualClass() {
|
|
94
|
-
return this.args.named.itemVisualClass ||
|
|
96
|
+
return this.args.named.itemVisualClass || 'is-activated';
|
|
95
97
|
}
|
|
96
98
|
|
|
97
99
|
get a11yItemName() {
|
|
@@ -179,7 +181,7 @@ export default class SortableGroupModifier extends Modifier {
|
|
|
179
181
|
|
|
180
182
|
// switch direction values to notify sortedItems to update, so it sorts by direction.
|
|
181
183
|
let value;
|
|
182
|
-
const dimension = direction ===
|
|
184
|
+
const dimension = direction === 'y' ? 'height' : 'width';
|
|
183
185
|
// DOWN or RIGHT
|
|
184
186
|
if (toIndex > fromIndex) {
|
|
185
187
|
value = item[direction];
|
|
@@ -206,13 +208,13 @@ export default class SortableGroupModifier extends Modifier {
|
|
|
206
208
|
const direction = this.direction;
|
|
207
209
|
const selectedItem = this._selectedItem;
|
|
208
210
|
|
|
209
|
-
if (direction ===
|
|
211
|
+
if (direction === 'y' && isDownArrowKey(event)) {
|
|
210
212
|
this.moveItem(selectedItem, 1);
|
|
211
|
-
} else if (direction ===
|
|
213
|
+
} else if (direction === 'y' && isUpArrowKey(event)) {
|
|
212
214
|
this.moveItem(selectedItem, -1);
|
|
213
|
-
} else if (direction ===
|
|
215
|
+
} else if (direction === 'x' && isLeftArrowKey(event)) {
|
|
214
216
|
this.moveItem(selectedItem, -1);
|
|
215
|
-
} else if (direction ===
|
|
217
|
+
} else if (direction === 'x' && isRightArrowKey(event)) {
|
|
216
218
|
this.moveItem(selectedItem, 1);
|
|
217
219
|
} else if (isEnterKey(event) || isSpaceKey(event)) {
|
|
218
220
|
// confirm will reset the selectedItem, so caching it here before we remove it.
|
|
@@ -233,7 +235,7 @@ export default class SortableGroupModifier extends Modifier {
|
|
|
233
235
|
const moves = this.moves;
|
|
234
236
|
if (moves && moves.length > 0) {
|
|
235
237
|
const sortedItems = this.sortedItems;
|
|
236
|
-
const itemElement = sortedItems[moves[0].fromIndex].element
|
|
238
|
+
const itemElement = sortedItems[moves[0].fromIndex].element;
|
|
237
239
|
this._focusItem(itemElement);
|
|
238
240
|
} else {
|
|
239
241
|
this._focusItem(_selectedItemElement);
|
|
@@ -285,7 +287,7 @@ export default class SortableGroupModifier extends Modifier {
|
|
|
285
287
|
const moves = this.moves;
|
|
286
288
|
while (moves.length > 0) {
|
|
287
289
|
const move = moves.pop();
|
|
288
|
-
this._move(move[1], move[0])
|
|
290
|
+
this._move(move[1], move[0]);
|
|
289
291
|
}
|
|
290
292
|
this._tearDownA11yApplicationContainer();
|
|
291
293
|
this._updateItemVisualIndicators(_selectedItem, false);
|
|
@@ -306,7 +308,7 @@ export default class SortableGroupModifier extends Modifier {
|
|
|
306
308
|
this.moves = [];
|
|
307
309
|
this._disableKeyboardReorderMode();
|
|
308
310
|
this._tearDownA11yApplicationContainer();
|
|
309
|
-
set(_selectedItem,
|
|
311
|
+
set(_selectedItem, 'wasDropped', true);
|
|
310
312
|
this.commit();
|
|
311
313
|
this._updateItemVisualIndicators(_selectedItem, false);
|
|
312
314
|
this._updateHandleVisualIndicators(_selectedItem, false);
|
|
@@ -335,7 +337,7 @@ export default class SortableGroupModifier extends Modifier {
|
|
|
335
337
|
const config = {
|
|
336
338
|
a11yItemName,
|
|
337
339
|
index: index,
|
|
338
|
-
maxLength
|
|
340
|
+
maxLength: sortedItems.length,
|
|
339
341
|
direction: this.direction,
|
|
340
342
|
delta,
|
|
341
343
|
};
|
|
@@ -363,7 +365,6 @@ export default class SortableGroupModifier extends Modifier {
|
|
|
363
365
|
* @param {Boolean} isActive to activate or deactivate the class.
|
|
364
366
|
*/
|
|
365
367
|
_updateItemVisualIndicators(item, isActive) {
|
|
366
|
-
|
|
367
368
|
const itemVisualClass = this.itemVisualClass;
|
|
368
369
|
|
|
369
370
|
if (!itemVisualClass || !item) {
|
|
@@ -397,7 +398,7 @@ export default class SortableGroupModifier extends Modifier {
|
|
|
397
398
|
const visualHandle = handle ? handle : item.element;
|
|
398
399
|
const visualKeys = direction === 'y' ? ['UP', 'DOWN'] : ['LEFT', 'RIGHT'];
|
|
399
400
|
|
|
400
|
-
visualKeys.forEach(visualKey => {
|
|
401
|
+
visualKeys.forEach((visualKey) => {
|
|
401
402
|
visualHandle.classList.remove(handleVisualClass[visualKey]);
|
|
402
403
|
});
|
|
403
404
|
|
|
@@ -447,7 +448,7 @@ export default class SortableGroupModifier extends Modifier {
|
|
|
447
448
|
* Sets up the group as an application and make it programmatically focusable.
|
|
448
449
|
*/
|
|
449
450
|
_setupA11yApplicationContainer() {
|
|
450
|
-
this.element.setAttribute(
|
|
451
|
+
this.element.setAttribute('role', 'application');
|
|
451
452
|
this.element.tabIndex = -1;
|
|
452
453
|
}
|
|
453
454
|
|
|
@@ -455,8 +456,8 @@ export default class SortableGroupModifier extends Modifier {
|
|
|
455
456
|
* Tears down the `role=application` container.
|
|
456
457
|
*/
|
|
457
458
|
_tearDownA11yApplicationContainer() {
|
|
458
|
-
this.element.removeAttribute(
|
|
459
|
-
this.element.removeAttribute(
|
|
459
|
+
this.element.removeAttribute('role');
|
|
460
|
+
this.element.removeAttribute('tabIndex');
|
|
460
461
|
}
|
|
461
462
|
|
|
462
463
|
_prepareKeyboardReorderMode() {
|
|
@@ -472,7 +473,7 @@ export default class SortableGroupModifier extends Modifier {
|
|
|
472
473
|
@default y
|
|
473
474
|
*/
|
|
474
475
|
get direction() {
|
|
475
|
-
return this.args.named.direction ||
|
|
476
|
+
return this.args.named.direction || 'y';
|
|
476
477
|
}
|
|
477
478
|
|
|
478
479
|
/**
|
|
@@ -488,7 +489,7 @@ export default class SortableGroupModifier extends Modifier {
|
|
|
488
489
|
return this.args.named.onChange;
|
|
489
490
|
}
|
|
490
491
|
|
|
491
|
-
@service('ember-sortable
|
|
492
|
+
@service('ember-sortable-internal-state')
|
|
492
493
|
sortableService;
|
|
493
494
|
|
|
494
495
|
/**
|
|
@@ -499,7 +500,7 @@ export default class SortableGroupModifier extends Modifier {
|
|
|
499
500
|
* @returns {*|string}
|
|
500
501
|
*/
|
|
501
502
|
get groupName() {
|
|
502
|
-
return this.args.named.groupName ||
|
|
503
|
+
return this.args.named.groupName || '_EmberSortableGroup';
|
|
503
504
|
}
|
|
504
505
|
|
|
505
506
|
_groupDef = this.sortableService.fetchGroup(this.groupName);
|
|
@@ -530,7 +531,7 @@ export default class SortableGroupModifier extends Modifier {
|
|
|
530
531
|
@property firstItemPosition
|
|
531
532
|
@type Number
|
|
532
533
|
*/
|
|
533
|
-
@computed(
|
|
534
|
+
@computed('direction', 'sortedItems')
|
|
534
535
|
get firstItemPosition() {
|
|
535
536
|
const direction = this.direction;
|
|
536
537
|
const sortedItems = this.sortedItems;
|
|
@@ -552,7 +553,7 @@ export default class SortableGroupModifier extends Modifier {
|
|
|
552
553
|
* Enables keyboard navigation
|
|
553
554
|
*/
|
|
554
555
|
@action
|
|
555
|
-
activateKeyDown(selectedItem){
|
|
556
|
+
activateKeyDown(selectedItem) {
|
|
556
557
|
this._selectedItem = selectedItem;
|
|
557
558
|
this.isKeyDownEnabled = true;
|
|
558
559
|
}
|
|
@@ -616,11 +617,11 @@ export default class SortableGroupModifier extends Modifier {
|
|
|
616
617
|
position = this.firstItemPosition;
|
|
617
618
|
}
|
|
618
619
|
|
|
619
|
-
sortedItems.forEach(item => {
|
|
620
|
+
sortedItems.forEach((item) => {
|
|
620
621
|
let dimension;
|
|
621
622
|
let direction = this.direction;
|
|
622
623
|
|
|
623
|
-
if (!item.isDragging) {
|
|
624
|
+
if (!isDestroyed(item) && !item.isDragging) {
|
|
624
625
|
set(item, direction, position);
|
|
625
626
|
}
|
|
626
627
|
|
|
@@ -646,8 +647,8 @@ export default class SortableGroupModifier extends Modifier {
|
|
|
646
647
|
@action
|
|
647
648
|
commit() {
|
|
648
649
|
const items = this.sortedItems;
|
|
649
|
-
const itemModels = items.map(item => item.model);
|
|
650
|
-
const draggedItem = items.find(item => item.wasDropped);
|
|
650
|
+
const itemModels = items.map((item) => item.model);
|
|
651
|
+
const draggedItem = items.find((item) => item.wasDropped);
|
|
651
652
|
let draggedModel;
|
|
652
653
|
|
|
653
654
|
if (draggedItem) {
|
|
@@ -676,16 +677,16 @@ export default class SortableGroupModifier extends Modifier {
|
|
|
676
677
|
delete this._firstItemPosition;
|
|
677
678
|
|
|
678
679
|
schedule('render', () => {
|
|
679
|
-
items.forEach(item => item.freeze());
|
|
680
|
+
items.forEach((item) => item.freeze());
|
|
680
681
|
});
|
|
681
682
|
|
|
682
683
|
schedule('afterRender', () => {
|
|
683
|
-
items.forEach(item => item.reset());
|
|
684
|
+
items.forEach((item) => item.reset());
|
|
684
685
|
});
|
|
685
686
|
|
|
686
687
|
next(() => {
|
|
687
688
|
schedule('render', () => {
|
|
688
|
-
items.forEach(item => item.thaw());
|
|
689
|
+
items.forEach((item) => item.thaw());
|
|
689
690
|
});
|
|
690
691
|
});
|
|
691
692
|
}
|
|
@@ -701,43 +702,53 @@ export default class SortableGroupModifier extends Modifier {
|
|
|
701
702
|
// end of API
|
|
702
703
|
|
|
703
704
|
addEventListener() {
|
|
704
|
-
this.element.addEventListener(
|
|
705
|
-
this.element.addEventListener(
|
|
705
|
+
this.element.addEventListener('keydown', this.keyDown);
|
|
706
|
+
this.element.addEventListener('focusout', this.focusOut);
|
|
706
707
|
}
|
|
707
708
|
|
|
708
709
|
removeEventListener() {
|
|
709
|
-
this.element.removeEventListener(
|
|
710
|
-
this.element.removeEventListener(
|
|
710
|
+
this.element.removeEventListener('keydown', this.keyDown);
|
|
711
|
+
this.element.removeEventListener('focusout', this.focusOut);
|
|
711
712
|
}
|
|
712
713
|
|
|
713
|
-
|
|
714
|
-
|
|
714
|
+
element;
|
|
715
|
+
didSetup = false;
|
|
715
716
|
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
this.addEventListener();
|
|
717
|
+
constructor(owner, args) {
|
|
718
|
+
super(owner, args);
|
|
719
|
+
registerDestructor(this, cleanup);
|
|
721
720
|
}
|
|
722
721
|
|
|
723
|
-
|
|
724
|
-
|
|
722
|
+
modify(element /*, positional, named*/) {
|
|
723
|
+
this.element = element;
|
|
725
724
|
|
|
726
|
-
|
|
727
|
-
this.announcer = this._createAnnouncer();
|
|
728
|
-
this.element.insertAdjacentElement('afterend', this.announcer);
|
|
725
|
+
this.removeEventListener();
|
|
729
726
|
|
|
730
|
-
|
|
731
|
-
|
|
727
|
+
if (!this.didSetup) {
|
|
728
|
+
this.announcer = this._createAnnouncer();
|
|
729
|
+
this.element.insertAdjacentElement('afterend', this.announcer);
|
|
730
|
+
this.sortableService.registerGroup(this.groupName, this);
|
|
732
731
|
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
732
|
+
this.didSetup = true;
|
|
733
|
+
}
|
|
734
|
+
|
|
735
|
+
if (this.disabled) {
|
|
736
|
+
return;
|
|
737
737
|
}
|
|
738
|
-
this.removeEventListener();
|
|
739
738
|
|
|
740
|
-
this.
|
|
739
|
+
this.addEventListener();
|
|
741
740
|
}
|
|
741
|
+
}
|
|
742
742
|
|
|
743
|
+
/**
|
|
744
|
+
*
|
|
745
|
+
* @param {SortableGroupModifier} instance
|
|
746
|
+
*/
|
|
747
|
+
function cleanup(instance) {
|
|
748
|
+
// todo cleanup the announcer
|
|
749
|
+
if (instance.announcer.parentNode) {
|
|
750
|
+
instance.announcer.parentNode.removeChild(instance.announcer);
|
|
751
|
+
}
|
|
752
|
+
instance.removeEventListener();
|
|
753
|
+
instance.sortableService.deregisterGroup(instance.groupName, instance);
|
|
743
754
|
}
|
|
@@ -1,26 +1,29 @@
|
|
|
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
|
+
didSetup = false;
|
|
18
19
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
20
|
+
modify(element /*, positional, named*/) {
|
|
21
|
+
if (!this.didSetup) {
|
|
22
|
+
// take the model and look up the registered element, the tell that element you are the handle
|
|
23
|
+
element.dataset.sortableHandle = true;
|
|
24
|
+
element.tabIndex = '0';
|
|
25
|
+
element.setAttribute('role', 'button');
|
|
26
|
+
this.didSetup = true;
|
|
27
|
+
}
|
|
24
28
|
}
|
|
25
|
-
|
|
26
29
|
}
|