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.
Files changed (38) hide show
  1. package/CHANGELOG.md +239 -108
  2. package/MIGRATION_GUIDE_MODIFIERS.md +20 -13
  3. package/MIGRATION_GUIDE_V2.md +20 -8
  4. package/README.md +94 -169
  5. package/RELEASE.md +60 -0
  6. package/V2_MIGRATION_RFC.md +188 -222
  7. package/addon/modifiers/sortable-group.js +69 -58
  8. package/addon/modifiers/sortable-handle.js +22 -19
  9. package/addon/modifiers/sortable-item.js +126 -82
  10. package/addon/services/ember-sortable.js +3 -6
  11. package/addon/system/scroll-parent.js +1 -1
  12. package/addon/utils/coordinate.js +1 -1
  13. package/addon/utils/css-calculation.js +2 -2
  14. package/addon/utils/defaults.js +4 -3
  15. package/addon/utils/keyboard.js +3 -12
  16. package/addon-test-support/helpers/drag.js +3 -11
  17. package/addon-test-support/helpers/index.js +1 -4
  18. package/addon-test-support/helpers/reorder.js +4 -6
  19. package/addon-test-support/utils/keyboard.js +3 -12
  20. package/addon-test-support/utils/offset.js +6 -6
  21. package/app/services/ember-sortable-internal-state.js +1 -0
  22. package/config/environment.js +2 -2
  23. package/index.js +0 -20
  24. package/package.json +56 -33
  25. package/.github/ISSUE_TEMPLATE/bug_report.md +0 -25
  26. package/.github/workflows/github_ci.yml +0 -69
  27. package/addon/components/sortable-group.js +0 -741
  28. package/addon/components/sortable-handle.js +0 -30
  29. package/addon/components/sortable-item.js +0 -770
  30. package/addon/styles/components/sortable-group.css +0 -9
  31. package/addon/templates/components/sortable-group.hbs +0 -16
  32. package/addon/templates/components/sortable-handle.hbs +0 -1
  33. package/addon/templates/components/sortable-item.hbs +0 -2
  34. package/addon/utils/transitionend.js +0 -22
  35. package/app/components/sortable-group.js +0 -3
  36. package/app/components/sortable-handle.js +0 -4
  37. package/app/components/sortable-item.js +0 -3
  38. 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 "../utils/keyboard";
12
- import {ANNOUNCEMENT_ACTION_TYPES} from "../utils/constant";
13
- import { defaultA11yAnnouncementConfig } from "../utils/defaults";
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 || "is-activated";
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 === "y" ? "height" : "width";
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 === "y" && isDownArrowKey(event)) {
211
+ if (direction === 'y' && isDownArrowKey(event)) {
210
212
  this.moveItem(selectedItem, 1);
211
- } else if (direction === "y" && isUpArrowKey(event)) {
213
+ } else if (direction === 'y' && isUpArrowKey(event)) {
212
214
  this.moveItem(selectedItem, -1);
213
- } else if (direction === "x" && isLeftArrowKey(event)) {
215
+ } else if (direction === 'x' && isLeftArrowKey(event)) {
214
216
  this.moveItem(selectedItem, -1);
215
- } else if (direction === "x" && isRightArrowKey(event)) {
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, "wasDropped", true);
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 : sortedItems.length,
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("role", "application");
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("role");
459
- this.element.removeAttribute("tabIndex");
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 || "y";
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@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 || "_EmberSortableGroup";
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("direction", "sortedItems")
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("keydown", this.keyDown);
705
- this.element.addEventListener("focusout", this.focusOut);
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("keydown", this.keyDown);
710
- this.element.removeEventListener("focusout", this.focusOut);
710
+ this.element.removeEventListener('keydown', this.keyDown);
711
+ this.element.removeEventListener('focusout', this.focusOut);
711
712
  }
712
713
 
713
- didReceiveArguments() {
714
- this.removeEventListener();
714
+ element;
715
+ didSetup = false;
715
716
 
716
- if (this.disabled) {
717
- return;
718
- }
719
-
720
- this.addEventListener();
717
+ constructor(owner, args) {
718
+ super(owner, args);
719
+ registerDestructor(this, cleanup);
721
720
  }
722
721
 
723
- didUpdateArguments() {
724
- }
722
+ modify(element /*, positional, named*/) {
723
+ this.element = element;
725
724
 
726
- didInstall() {
727
- this.announcer = this._createAnnouncer();
728
- this.element.insertAdjacentElement('afterend', this.announcer);
725
+ this.removeEventListener();
729
726
 
730
- this.sortableService.registerGroup(this.groupName, this);
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
- willRemove() {
734
- // todo cleanup the announcer
735
- if(this.announcer.parentNode) {
736
- this.announcer.parentNode.removeChild(this.announcer);
732
+ this.didSetup = true;
733
+ }
734
+
735
+ if (this.disabled) {
736
+ return;
737
737
  }
738
- this.removeEventListener();
739
738
 
740
- this.sortableService.deregisterGroup(this.groupName, 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}}>&varr;</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}}>&varr;</span>
13
+ * </li>
14
+ * {{/each}}
15
+ * </ol>
16
+ */
17
17
  export default class SortableHandleModifier extends Modifier {
18
+ didSetup = false;
18
19
 
19
- didInstall() {
20
- // 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 = "0";
23
- this.element.setAttribute("role", "button");
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
  }