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
package/V2_MIGRATION_RFC.md
CHANGED
|
@@ -1,15 +1,18 @@
|
|
|
1
|
-
# Ember Sortable
|
|
1
|
+
# Ember Sortable V2 Migration RFC
|
|
2
|
+
|
|
3
|
+
## Author
|
|
2
4
|
|
|
3
|
-
## Author ##
|
|
4
5
|
[ygongdev](https://github.com/ygongdev)
|
|
5
6
|
|
|
6
|
-
## Credits
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
7
|
+
## Credits
|
|
8
|
+
|
|
9
|
+
- [jgwhite](https://github.com/jgwhite)
|
|
10
|
+
- [H1D](https://github.com/H1D)
|
|
11
|
+
- [chriskrycho](https://github.com/chriskrycho)
|
|
12
|
+
- [Andrew Lee](https://github.com/drewlee)
|
|
13
|
+
|
|
14
|
+
## Table of Content
|
|
11
15
|
|
|
12
|
-
## Table of Content ##
|
|
13
16
|
1. [Problem Statement](#problem-statement)
|
|
14
17
|
2. [Features](#features)
|
|
15
18
|
3. [Design/Architecture](#designarchitecture)
|
|
@@ -18,51 +21,61 @@
|
|
|
18
21
|
6. [Release Plan](#release-plan)
|
|
19
22
|
7. [Questions to be addressed](#questions-to-be-addressed)
|
|
20
23
|
|
|
21
|
-
## Problem Statement
|
|
24
|
+
## Problem Statement
|
|
25
|
+
|
|
22
26
|
`ember-sortable` has been falling behind in the adoption of the on-going `Ember` upgrades. It is currently not in the right condition, in which we can upgrade the `addon` without many blockers.
|
|
23
27
|
|
|
24
28
|
This RFC is meant to describe a high level overview of a new `ember-sortable`, which will help push us adopt many of the new `Ember` features.
|
|
25
29
|
|
|
26
30
|
This RFC is **NOT** meant to show the final implementation details as implementation will be re-iterated and improved over time.
|
|
27
31
|
|
|
28
|
-
## Features
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
32
|
+
## Features
|
|
33
|
+
|
|
34
|
+
- Baked in accessibility support
|
|
35
|
+
- Up/down and left/right keyboard navigation
|
|
36
|
+
- Screen reader announcement
|
|
37
|
+
- Focus management
|
|
38
|
+
- Visual indicators
|
|
39
|
+
- Direction-agnostic (in terms of mouse drag)
|
|
40
|
+
- Keyboard navigation is limited to up/down/left/right
|
|
41
|
+
- Allows nested sortable elements
|
|
42
|
+
- Adoption of modern Ember testing infrastructure
|
|
43
|
+
- Animation
|
|
44
|
+
- Built with composability and customizability in mind
|
|
45
|
+
|
|
46
|
+
## Design/Architecture
|
|
47
|
+
|
|
48
|
+
### 1. Contextual Components
|
|
49
|
+
|
|
43
50
|
The new ember sortable will be designed using contextual components.
|
|
44
51
|
It will be made up of 3 main components:
|
|
45
52
|
|
|
46
|
-
#### Sortable-group
|
|
53
|
+
#### Sortable-group
|
|
54
|
+
|
|
47
55
|
Represents the entire sortable component.
|
|
48
|
-
* requires a group of models to sort. The model will be a shallow copy and will not modify the given group of models.
|
|
49
|
-
* contains all of main logic that makes `ember-sortable` work.
|
|
50
|
-
* yields `sortable-item` and other properties as needed.
|
|
51
56
|
|
|
52
|
-
|
|
57
|
+
- requires a group of models to sort. The model will be a shallow copy and will not modify the given group of models.
|
|
58
|
+
- contains all of main logic that makes `ember-sortable` work.
|
|
59
|
+
- yields `sortable-item` and other properties as needed.
|
|
60
|
+
|
|
61
|
+
#### Sortable-item
|
|
62
|
+
|
|
53
63
|
Represents the individual `model` of the group of models.
|
|
54
|
-
* yields `sortable-handle` and other properties as needed.
|
|
55
64
|
|
|
56
|
-
|
|
65
|
+
- yields `sortable-handle` and other properties as needed.
|
|
66
|
+
|
|
67
|
+
#### Sortable-handle
|
|
68
|
+
|
|
57
69
|
Represents the handle of each `sortable-item`.
|
|
58
70
|
|
|
59
71
|
This is the bread and butter of the entire component because it is the entrypoint that allows us to start sorting.
|
|
60
72
|
|
|
61
73
|
This component hooks up your custom handle to the mainframe, `sortable-group`.
|
|
62
74
|
|
|
63
|
-
|
|
75
|
+
- yields other properties as needed.
|
|
76
|
+
|
|
77
|
+
#### Conceptual Example
|
|
64
78
|
|
|
65
|
-
#### Conceptual Example ####
|
|
66
79
|
```javascript
|
|
67
80
|
{{#ember-sortable::sortable-group
|
|
68
81
|
modelGroup=modelGroup
|
|
@@ -87,73 +100,91 @@ This component hooks up your custom handle to the mainframe, `sortable-group`.
|
|
|
87
100
|
{{/ember-sortable::sortable-group}}
|
|
88
101
|
```
|
|
89
102
|
|
|
90
|
-
### 2. Event driven
|
|
103
|
+
### 2. Event driven
|
|
104
|
+
|
|
91
105
|
Similar to the current `ember-sortable`, the sorting behavior is going to be mainly based on `events`. `Animation` might be an exception.
|
|
92
106
|
We will utilize the [Drag and Drop API](https://developer.mozilla.org/en-US/docs/Web/API/HTML_Drag_and_Drop_API), [Keyboard Event API](https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent), and/or [Mouse Event API](https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent).
|
|
93
107
|
|
|
94
|
-
### 3. Animation
|
|
108
|
+
### 3. Animation
|
|
109
|
+
|
|
95
110
|
For `animation`, we can explore different methods, e.g custom implementation, external animation library. However, the final decision will hopefully satisify the following:
|
|
96
|
-
* minimize expensive DOM operations, e.g `getComputedStyle`, `Ember runloop scheduling`.
|
|
97
|
-
* If we want to test animations, the `animation` have to be deterministic enough for us to test reliably.
|
|
98
111
|
|
|
99
|
-
|
|
112
|
+
- minimize expensive DOM operations, e.g `getComputedStyle`, `Ember runloop scheduling`.
|
|
113
|
+
- If we want to test animations, the `animation` have to be deterministic enough for us to test reliably.
|
|
114
|
+
|
|
115
|
+
### 4. Accessibility
|
|
116
|
+
|
|
100
117
|
This section provides a high level overview of how we will address the issue of `accessibility`.
|
|
101
118
|
|
|
102
119
|
**The accessibility solutions described are directly referenced from this awesome [codepen demo](https://codepen.io/drewlee/project/full/XWNLeE) made by Andrew Lee.**
|
|
103
120
|
|
|
104
|
-
#### Keyboard Navigation
|
|
121
|
+
#### Keyboard Navigation
|
|
122
|
+
|
|
105
123
|
**1. Keyboard Reorder Mode**
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
124
|
+
|
|
125
|
+
- To initiate `keyboardReorderMode`, a `sortable-handle` must be `focused` and an `Enter/Space` must be pressed.
|
|
126
|
+
- This operation selects `sortable-item` parent of the `sortable-handle` and enable sorting the `sortable-item` within the `sortable-group` via up/down/left/right arrow keys.
|
|
127
|
+
- We will set `sortable-group` as a dedicated `container` by adding `role` attribute and programmatically set the `focus` onto it.
|
|
128
|
+
- We will create a `visual color indicator` around the selected `sortable-item`. A screen reader announcement will also be made to inform the user that the `sortable-item` has been selected and `sorting` has been enabled.
|
|
129
|
+
- To create `visual indicators`, `sortable-item` will append some `classes`, which by default tries to create `arrow` visual indcators around `sortable-item`. However, consumers are free to override the `class` to customize their own. `Visual indicators` will move as the`sortable-item` moves.
|
|
111
130
|
|
|
112
131
|
**2. Commit**
|
|
113
|
-
|
|
132
|
+
|
|
133
|
+
- Every navigation will reorder the components in the UI. However, the reorder will not be `committed` until an `Enter/Space` key has been pressed. If committed, the `focus` will move from the `sortable-group` back to the `sortable-handle`. `role` will be removed.
|
|
114
134
|
|
|
115
135
|
**3. Reset**
|
|
116
|
-
* The user can also exit `keyboardReorderMode` via `Escape` key or on focus lost. This will reset the reordering back to its initial state and `focus` is retained on the `sortable-handle`. `role` will be removed.
|
|
117
|
-
* We should not need animation for this.
|
|
118
136
|
|
|
119
|
-
|
|
137
|
+
- The user can also exit `keyboardReorderMode` via `Escape` key or on focus lost. This will reset the reordering back to its initial state and `focus` is retained on the `sortable-handle`. `role` will be removed.
|
|
138
|
+
- We should not need animation for this.
|
|
139
|
+
|
|
140
|
+
#### Drag Drop
|
|
141
|
+
|
|
120
142
|
Drag drop will retain the same behavior as the current `ember-sortable`.
|
|
121
143
|
|
|
122
|
-
#### Screen Reader Announcements
|
|
123
|
-
|
|
124
|
-
|
|
144
|
+
#### Screen Reader Announcements
|
|
145
|
+
|
|
146
|
+
- Internally, we can use a `announceActionConfig` object to map an `action` to a `text`. We can have some default texts.
|
|
147
|
+
- To support `i18n`, the consumer can supply their own `announceActionConfig` with their translated strings.
|
|
148
|
+
|
|
149
|
+
### 5. Utility Classes and Functions
|
|
125
150
|
|
|
126
|
-
### 5. Utility Classes and Functions ###
|
|
127
151
|
We should create a utility class to abstract as much work as we can from the components.
|
|
128
152
|
|
|
129
153
|
For example, a `keyboardManager` class can be used to maintain a history of our operations as well as provide the functionalities to perform the reordering logic during `keyboard navigation`.
|
|
130
154
|
|
|
131
|
-
### 6. Testing
|
|
155
|
+
### 6. Testing
|
|
156
|
+
|
|
132
157
|
The current `ember-sortable` implements two test helpers
|
|
133
|
-
|
|
134
|
-
|
|
158
|
+
|
|
159
|
+
- reorder
|
|
160
|
+
- drag
|
|
135
161
|
|
|
136
162
|
Both are heavily DOM calculation driven and can sometimes be hard and unreliable to use.
|
|
137
163
|
|
|
138
164
|
I propose new `event driven` test helpers and perhaps remove `reorder` as it is just a combination of `drag`. Internally, these would be just triggering `events`, which should be determinstic to test.
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
165
|
+
|
|
166
|
+
- drag (for mouse)
|
|
167
|
+
- move (for keyboard)
|
|
168
|
+
- reorder?
|
|
142
169
|
|
|
143
170
|
We can implement another `drag` that is dedicated to testing `animation`.
|
|
144
171
|
|
|
145
|
-
## API
|
|
172
|
+
## API
|
|
173
|
+
|
|
146
174
|
TBD
|
|
147
175
|
|
|
148
|
-
## Implementation
|
|
176
|
+
## Implementation
|
|
177
|
+
|
|
149
178
|
**NOTE**
|
|
150
179
|
This is an example to help give an idea of what it could look like.
|
|
151
180
|
This is **NOT** meant to be the final implementation.
|
|
152
181
|
This example does **NOT** contain how we would handle animation and horizontal keyboard sorting.
|
|
153
182
|
While creating this, I used the name `draggable` instead of `ember-sortable`.
|
|
154
183
|
|
|
155
|
-
### draggable-group
|
|
156
|
-
|
|
184
|
+
### draggable-group
|
|
185
|
+
|
|
186
|
+
#### hbs
|
|
187
|
+
|
|
157
188
|
```javascript
|
|
158
189
|
{{yield
|
|
159
190
|
(hash
|
|
@@ -172,7 +203,8 @@ While creating this, I used the name `draggable` instead of `ember-sortable`.
|
|
|
172
203
|
}}
|
|
173
204
|
```
|
|
174
205
|
|
|
175
|
-
#### js
|
|
206
|
+
#### js
|
|
207
|
+
|
|
176
208
|
```javascript
|
|
177
209
|
/**
|
|
178
210
|
* This component supports re-ordering items in a group via drag-drop and keyboard navigation.
|
|
@@ -210,11 +242,7 @@ While creating this, I used the name `draggable` instead of `ember-sortable`.
|
|
|
210
242
|
*/
|
|
211
243
|
export default Component.extend({
|
|
212
244
|
layout,
|
|
213
|
-
attributeBindings: [
|
|
214
|
-
'tabindex',
|
|
215
|
-
'role',
|
|
216
|
-
'dataTestDragDropDraggableGroup:data-test-drag-drop-draggable-group',
|
|
217
|
-
],
|
|
245
|
+
attributeBindings: ['tabindex', 'role', 'dataTestDragDropDraggableGroup:data-test-drag-drop-draggable-group'],
|
|
218
246
|
// data-test selector
|
|
219
247
|
dataTestDragDropDraggableGroup: true,
|
|
220
248
|
/**
|
|
@@ -264,10 +292,7 @@ export default Component.extend({
|
|
|
264
292
|
* @param {Event} evt a HTML event
|
|
265
293
|
*/
|
|
266
294
|
keyDown(evt) {
|
|
267
|
-
const isKeyboardReorderModeEnabled = get(
|
|
268
|
-
this,
|
|
269
|
-
'isKeyboardReorderModeEnabled'
|
|
270
|
-
);
|
|
295
|
+
const isKeyboardReorderModeEnabled = get(this, 'isKeyboardReorderModeEnabled');
|
|
271
296
|
|
|
272
297
|
if (!isKeyboardReorderModeEnabled && (isEnterKey(evt) || isSpaceKey(evt))) {
|
|
273
298
|
this._enableKeyboardReorderMode();
|
|
@@ -286,11 +311,7 @@ export default Component.extend({
|
|
|
286
311
|
}
|
|
287
312
|
|
|
288
313
|
if (isKeyboardReorderModeEnabled) {
|
|
289
|
-
const { selectedModelIndex, maxIndex } = getProperties(
|
|
290
|
-
this,
|
|
291
|
-
'selectedModelIndex',
|
|
292
|
-
'maxIndex'
|
|
293
|
-
);
|
|
314
|
+
const { selectedModelIndex, maxIndex } = getProperties(this, 'selectedModelIndex', 'maxIndex');
|
|
294
315
|
if (isDownArrowKey(evt)) {
|
|
295
316
|
const newIndex = Math.min(selectedModelIndex + 1, maxIndex - 1);
|
|
296
317
|
this._moveItem(selectedModelIndex, newIndex);
|
|
@@ -321,9 +342,7 @@ export default Component.extend({
|
|
|
321
342
|
|
|
322
343
|
readDOM(() => {
|
|
323
344
|
if (IS_BROWSER) {
|
|
324
|
-
this.element
|
|
325
|
-
.querySelectorAll(`[${DRAGGABLE_HANDLE_ATTRIBUTE}]`)
|
|
326
|
-
[selectedModelIndex].focus();
|
|
345
|
+
this.element.querySelectorAll(`[${DRAGGABLE_HANDLE_ATTRIBUTE}]`)[selectedModelIndex].focus();
|
|
327
346
|
set(this, 'isRetainingFocus', false);
|
|
328
347
|
}
|
|
329
348
|
});
|
|
@@ -339,9 +358,7 @@ export default Component.extend({
|
|
|
339
358
|
|
|
340
359
|
readDOM(() => {
|
|
341
360
|
if (IS_BROWSER) {
|
|
342
|
-
this.element
|
|
343
|
-
.querySelectorAll(`[${DRAGGABLE_HANDLE_ATTRIBUTE}]`)
|
|
344
|
-
[lastIndex].focus();
|
|
361
|
+
this.element.querySelectorAll(`[${DRAGGABLE_HANDLE_ATTRIBUTE}]`)[lastIndex].focus();
|
|
345
362
|
set(this, 'isRetainingFocus', false);
|
|
346
363
|
}
|
|
347
364
|
});
|
|
@@ -357,11 +374,7 @@ export default Component.extend({
|
|
|
357
374
|
* If focus management is not finished and the current focused element is not the handle or descendant of the handle: Cancel
|
|
358
375
|
*/
|
|
359
376
|
focusOut(evt) {
|
|
360
|
-
if (
|
|
361
|
-
IS_BROWSER &&
|
|
362
|
-
!get(this, 'isRetainingFocus') &&
|
|
363
|
-
!this._isElementWithinHandle(document.activeElement)
|
|
364
|
-
) {
|
|
377
|
+
if (IS_BROWSER && !get(this, 'isRetainingFocus') && !this._isElementWithinHandle(document.activeElement)) {
|
|
365
378
|
this._cancelKeyboardSelection();
|
|
366
379
|
}
|
|
367
380
|
evt.stopPropagation();
|
|
@@ -414,27 +427,17 @@ export default Component.extend({
|
|
|
414
427
|
const dropTarget = evt.target;
|
|
415
428
|
evt.preventDefault();
|
|
416
429
|
if (this._isElementWithinDrop(dropTarget)) {
|
|
417
|
-
const {
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
'selectedModel',
|
|
427
|
-
'selectedModelIndex',
|
|
428
|
-
'targetModel',
|
|
429
|
-
'targetModelIndex'
|
|
430
|
-
);
|
|
430
|
+
const { keyboardReorderManager, selectedModel, selectedModelIndex, targetModel, targetModelIndex } =
|
|
431
|
+
getProperties(
|
|
432
|
+
this,
|
|
433
|
+
'keyboardReorderManager',
|
|
434
|
+
'selectedModel',
|
|
435
|
+
'selectedModelIndex',
|
|
436
|
+
'targetModel',
|
|
437
|
+
'targetModelIndex'
|
|
438
|
+
);
|
|
431
439
|
this._moveItem(selectedModelIndex, targetModelIndex);
|
|
432
|
-
tryInvoke(this, 'onSubmit', [
|
|
433
|
-
selectedModel,
|
|
434
|
-
selectedModelIndex,
|
|
435
|
-
targetModel,
|
|
436
|
-
targetModelIndex,
|
|
437
|
-
]);
|
|
440
|
+
tryInvoke(this, 'onSubmit', [selectedModel, selectedModelIndex, targetModel, targetModelIndex]);
|
|
438
441
|
keyboardReorderManager.clearRecord();
|
|
439
442
|
}
|
|
440
443
|
this._resetModelSelection();
|
|
@@ -452,13 +455,7 @@ export default Component.extend({
|
|
|
452
455
|
* 6. Announce the change.
|
|
453
456
|
*/
|
|
454
457
|
_confirmKeyboardSelection() {
|
|
455
|
-
const {
|
|
456
|
-
keyboardReorderManager,
|
|
457
|
-
selectedModel,
|
|
458
|
-
selectedModelIndex,
|
|
459
|
-
targetModel,
|
|
460
|
-
targetModelIndex,
|
|
461
|
-
} = getProperties(
|
|
458
|
+
const { keyboardReorderManager, selectedModel, selectedModelIndex, targetModel, targetModelIndex } = getProperties(
|
|
462
459
|
this,
|
|
463
460
|
'keyboardReorderManager',
|
|
464
461
|
'selectedModel',
|
|
@@ -471,12 +468,7 @@ export default Component.extend({
|
|
|
471
468
|
this._disableKeyboardReorderMode();
|
|
472
469
|
this._tearDownA11yApplicationContainer();
|
|
473
470
|
|
|
474
|
-
tryInvoke(this, 'onSubmit', [
|
|
475
|
-
selectedModel,
|
|
476
|
-
selectedModelIndex,
|
|
477
|
-
targetModel,
|
|
478
|
-
targetModelIndex,
|
|
479
|
-
]);
|
|
471
|
+
tryInvoke(this, 'onSubmit', [selectedModel, selectedModelIndex, targetModel, targetModelIndex]);
|
|
480
472
|
|
|
481
473
|
this._resetModelSelection();
|
|
482
474
|
this._announceAction(ANNOUNCEMENT_ACTION_TYPES.CONFIRM);
|
|
@@ -576,9 +568,7 @@ export default Component.extend({
|
|
|
576
568
|
* @param {Element} element a DOM element.
|
|
577
569
|
*/
|
|
578
570
|
_isElementWithinHandle(element) {
|
|
579
|
-
return element.closest(
|
|
580
|
-
`#${this.element.id} [${DRAGGABLE_HANDLE_ATTRIBUTE}]`
|
|
581
|
-
);
|
|
571
|
+
return element.closest(`#${this.element.id} [${DRAGGABLE_HANDLE_ATTRIBUTE}]`);
|
|
582
572
|
},
|
|
583
573
|
|
|
584
574
|
/**
|
|
@@ -599,10 +589,7 @@ export default Component.extend({
|
|
|
599
589
|
* @return {Function}
|
|
600
590
|
*/
|
|
601
591
|
_geti18nMessage(key, data) {
|
|
602
|
-
const messageRenderer = get(this, 'i18n').getMessageRenderer(
|
|
603
|
-
get(this, 'layout'),
|
|
604
|
-
key
|
|
605
|
-
);
|
|
592
|
+
const messageRenderer = get(this, 'i18n').getMessageRenderer(get(this, 'layout'), key);
|
|
606
593
|
return messageRenderer([data]);
|
|
607
594
|
},
|
|
608
595
|
|
|
@@ -703,8 +690,10 @@ export default Component.extend({
|
|
|
703
690
|
});
|
|
704
691
|
```
|
|
705
692
|
|
|
706
|
-
### draggable-item
|
|
707
|
-
|
|
693
|
+
### draggable-item
|
|
694
|
+
|
|
695
|
+
#### hbs
|
|
696
|
+
|
|
708
697
|
```javascript
|
|
709
698
|
{{yield
|
|
710
699
|
(hash
|
|
@@ -721,7 +710,8 @@ export default Component.extend({
|
|
|
721
710
|
}}
|
|
722
711
|
```
|
|
723
712
|
|
|
724
|
-
#### js
|
|
713
|
+
#### js
|
|
714
|
+
|
|
725
715
|
```javascript
|
|
726
716
|
/**
|
|
727
717
|
* This private component represents the individual model of `draggable-group`.
|
|
@@ -762,11 +752,7 @@ export default Component.extend({
|
|
|
762
752
|
* @param {Integer} index the position of this model in the DOM.
|
|
763
753
|
*/
|
|
764
754
|
isSelected: computed('selectedModelIndex', 'index', function getIsSelected() {
|
|
765
|
-
const { selectedModelIndex, index } = getProperties(
|
|
766
|
-
this,
|
|
767
|
-
'selectedModelIndex',
|
|
768
|
-
'index'
|
|
769
|
-
);
|
|
755
|
+
const { selectedModelIndex, index } = getProperties(this, 'selectedModelIndex', 'index');
|
|
770
756
|
|
|
771
757
|
return selectedModelIndex === index;
|
|
772
758
|
}),
|
|
@@ -790,12 +776,7 @@ export default Component.extend({
|
|
|
790
776
|
* @param {Event} evt a HTML event.
|
|
791
777
|
*/
|
|
792
778
|
keyDown(evt) {
|
|
793
|
-
const {
|
|
794
|
-
isKeyboardReorderModeEnabled,
|
|
795
|
-
model,
|
|
796
|
-
selectedModelIndex,
|
|
797
|
-
index,
|
|
798
|
-
} = getProperties(
|
|
779
|
+
const { isKeyboardReorderModeEnabled, model, selectedModelIndex, index } = getProperties(
|
|
799
780
|
this,
|
|
800
781
|
'isKeyboardReorderModeEnabled',
|
|
801
782
|
'model',
|
|
@@ -805,8 +786,7 @@ export default Component.extend({
|
|
|
805
786
|
|
|
806
787
|
if (
|
|
807
788
|
selectedModelIndex < 0 &&
|
|
808
|
-
(isKeyboardReorderModeEnabled ||
|
|
809
|
-
(!isKeyboardReorderModeEnabled && (isEnterKey(evt) || isSpaceKey(evt))))
|
|
789
|
+
(isKeyboardReorderModeEnabled || (!isKeyboardReorderModeEnabled && (isEnterKey(evt) || isSpaceKey(evt))))
|
|
810
790
|
) {
|
|
811
791
|
this.onSelect(model, index);
|
|
812
792
|
}
|
|
@@ -837,14 +817,8 @@ export default Component.extend({
|
|
|
837
817
|
* Asserts that required properties are defined correctly.
|
|
838
818
|
*/
|
|
839
819
|
_assertProperties() {
|
|
840
|
-
assert(
|
|
841
|
-
|
|
842
|
-
get(this, 'isKeyboardReorderModeEnabled') !== undefined
|
|
843
|
-
);
|
|
844
|
-
assert(
|
|
845
|
-
'selectedModelIndex is required',
|
|
846
|
-
get(this, 'selectedModelIndex') !== undefined
|
|
847
|
-
);
|
|
820
|
+
assert('isKeyboardReorderModeEnabled is required', get(this, 'isKeyboardReorderModeEnabled') !== undefined);
|
|
821
|
+
assert('selectedModelIndex is required', get(this, 'selectedModelIndex') !== undefined);
|
|
848
822
|
assert('maxIndex is required', get(this, 'maxIndex') !== undefined);
|
|
849
823
|
assert('itemName is required', get(this, 'itemName') !== undefined);
|
|
850
824
|
},
|
|
@@ -881,13 +855,21 @@ export default Component.extend({
|
|
|
881
855
|
});
|
|
882
856
|
```
|
|
883
857
|
|
|
884
|
-
### draggable-handle
|
|
885
|
-
|
|
858
|
+
### draggable-handle
|
|
859
|
+
|
|
860
|
+
#### hbs
|
|
861
|
+
|
|
886
862
|
```javascript
|
|
887
|
-
{
|
|
888
|
-
|
|
863
|
+
{
|
|
864
|
+
{
|
|
865
|
+
yield;
|
|
866
|
+
}
|
|
867
|
+
}
|
|
868
|
+
<span class="visually-hidden">{{ a11yText }}</span>;
|
|
889
869
|
```
|
|
890
|
-
|
|
870
|
+
|
|
871
|
+
#### js
|
|
872
|
+
|
|
891
873
|
```javascript
|
|
892
874
|
/**
|
|
893
875
|
* This private component represents the `handle` for each `draggable-item` of a `draggable-group`.
|
|
@@ -908,10 +890,7 @@ export default Component.extend({
|
|
|
908
890
|
*/
|
|
909
891
|
tagName: 'div',
|
|
910
892
|
tabindex: 0,
|
|
911
|
-
classNameBindings: [
|
|
912
|
-
'showA11yPreviousArrow:drag-drop__handle-previous',
|
|
913
|
-
'showA11yNextArrow:drag-drop__handle-next',
|
|
914
|
-
],
|
|
893
|
+
classNameBindings: ['showA11yPreviousArrow:drag-drop__handle-previous', 'showA11yNextArrow:drag-drop__handle-next'],
|
|
915
894
|
attributeBindings: [
|
|
916
895
|
`dataDragDropHandle:${DRAGGABLE_HANDLE_ATTRIBUTE}`,
|
|
917
896
|
'tabindex',
|
|
@@ -937,19 +916,13 @@ export default Component.extend({
|
|
|
937
916
|
'selectedIndex',
|
|
938
917
|
'isKeyboardReorderModeEnabled',
|
|
939
918
|
function getShowA11yPreviousArrow() {
|
|
940
|
-
const {
|
|
941
|
-
index,
|
|
942
|
-
selectedIndex,
|
|
943
|
-
isKeyboardReorderModeEnabled,
|
|
944
|
-
} = getProperties(
|
|
919
|
+
const { index, selectedIndex, isKeyboardReorderModeEnabled } = getProperties(
|
|
945
920
|
this,
|
|
946
921
|
'index',
|
|
947
922
|
'selectedIndex',
|
|
948
923
|
'isKeyboardReorderModeEnabled'
|
|
949
924
|
);
|
|
950
|
-
return
|
|
951
|
-
isKeyboardReorderModeEnabled && selectedIndex === index && index > 0
|
|
952
|
-
);
|
|
925
|
+
return isKeyboardReorderModeEnabled && selectedIndex === index && index > 0;
|
|
953
926
|
}
|
|
954
927
|
),
|
|
955
928
|
|
|
@@ -969,23 +942,14 @@ export default Component.extend({
|
|
|
969
942
|
'maxIndex',
|
|
970
943
|
'isKeyboardReorderModeEnabled',
|
|
971
944
|
function getShowA11yNextArrow() {
|
|
972
|
-
const {
|
|
973
|
-
index,
|
|
974
|
-
selectedIndex,
|
|
975
|
-
maxIndex,
|
|
976
|
-
isKeyboardReorderModeEnabled,
|
|
977
|
-
} = getProperties(
|
|
945
|
+
const { index, selectedIndex, maxIndex, isKeyboardReorderModeEnabled } = getProperties(
|
|
978
946
|
this,
|
|
979
947
|
'index',
|
|
980
948
|
'maxIndex',
|
|
981
949
|
'selectedIndex',
|
|
982
950
|
'isKeyboardReorderModeEnabled'
|
|
983
951
|
);
|
|
984
|
-
return
|
|
985
|
-
isKeyboardReorderModeEnabled &&
|
|
986
|
-
selectedIndex === index &&
|
|
987
|
-
index < maxIndex - 1
|
|
988
|
-
);
|
|
952
|
+
return isKeyboardReorderModeEnabled && selectedIndex === index && index < maxIndex - 1;
|
|
989
953
|
}
|
|
990
954
|
),
|
|
991
955
|
|
|
@@ -1010,14 +974,8 @@ export default Component.extend({
|
|
|
1010
974
|
assert(`a11yText is required`, get(this, 'a11yText'));
|
|
1011
975
|
assert(`index is required`, get(this, 'index') !== undefined);
|
|
1012
976
|
assert(`maxIndex is required`, get(this, 'maxIndex') !== undefined);
|
|
1013
|
-
assert(
|
|
1014
|
-
|
|
1015
|
-
get(this, 'selectedIndex') !== undefined
|
|
1016
|
-
);
|
|
1017
|
-
assert(
|
|
1018
|
-
`isKeyboardReorderModeEnabled is required`,
|
|
1019
|
-
get(this, 'isKeyboardReorderModeEnabled') !== undefined
|
|
1020
|
-
);
|
|
977
|
+
assert(`selectedIndex is required`, get(this, 'selectedIndex') !== undefined);
|
|
978
|
+
assert(`isKeyboardReorderModeEnabled is required`, get(this, 'isKeyboardReorderModeEnabled') !== undefined);
|
|
1021
979
|
},
|
|
1022
980
|
|
|
1023
981
|
/**
|
|
@@ -1036,19 +994,21 @@ export default Component.extend({
|
|
|
1036
994
|
});
|
|
1037
995
|
```
|
|
1038
996
|
|
|
1039
|
-
### utils/constants.js
|
|
997
|
+
### utils/constants.js
|
|
998
|
+
|
|
1040
999
|
```javascript
|
|
1041
|
-
export const DRAGGABLE_HANDLE_ATTRIBUTE
|
|
1042
|
-
export const DRAGGABLE_ITEM_ATTRIBUTE =
|
|
1000
|
+
export const DRAGGABLE_HANDLE_ATTRIBUTE = 'data-drag-drop-draggable-handle';
|
|
1001
|
+
export const DRAGGABLE_ITEM_ATTRIBUTE = 'data-drag-drop-draggable-item';
|
|
1043
1002
|
export const ANNOUNCEMENT_ACTION_TYPES = {
|
|
1044
1003
|
ACTIVATE: true,
|
|
1045
1004
|
MOVE: true,
|
|
1046
1005
|
CONFIRM: true,
|
|
1047
1006
|
CANCEL: true,
|
|
1048
|
-
}
|
|
1007
|
+
};
|
|
1049
1008
|
```
|
|
1050
1009
|
|
|
1051
|
-
### utils/keyboard-reorder-manager.js
|
|
1010
|
+
### utils/keyboard-reorder-manager.js
|
|
1011
|
+
|
|
1052
1012
|
```javascript
|
|
1053
1013
|
class ReorderRecord {
|
|
1054
1014
|
constructor(fromIndex, toIndex) {
|
|
@@ -1100,35 +1060,41 @@ export default class KeyboardReorderManager {
|
|
|
1100
1060
|
}
|
|
1101
1061
|
```
|
|
1102
1062
|
|
|
1103
|
-
## Release Plan
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1063
|
+
## Release Plan
|
|
1064
|
+
|
|
1065
|
+
### 2.0
|
|
1066
|
+
|
|
1067
|
+
- New API with backward incompatible changes
|
|
1068
|
+
- Drag drop
|
|
1069
|
+
- Animation
|
|
1070
|
+
- Test helper
|
|
1071
|
+
- Test Infrastructure Modernization
|
|
1072
|
+
- remove `registerAsyncHelper`
|
|
1073
|
+
- `module` and `setupHooks` syntax
|
|
1074
|
+
- Remove `jQuery` in favor of vanilla.
|
|
1075
|
+
- Migration Guide
|
|
1076
|
+
- 1.x.x -> 2.x.x
|
|
1077
|
+
|
|
1078
|
+
### 2.1
|
|
1079
|
+
|
|
1080
|
+
- Keyboard support
|
|
1081
|
+
- Keyboard navigation (left/right/up/down)
|
|
1082
|
+
- Commit
|
|
1083
|
+
- Reset
|
|
1084
|
+
|
|
1085
|
+
### 2.2
|
|
1086
|
+
|
|
1087
|
+
- Accessibility support
|
|
1088
|
+
- Screen reader announcements
|
|
1089
|
+
- Focus management
|
|
1090
|
+
- Semantic markup and attributes
|
|
1091
|
+
|
|
1092
|
+
### 2.3
|
|
1093
|
+
|
|
1094
|
+
- Nesting support
|
|
1095
|
+
|
|
1096
|
+
## Questions to be addressed
|
|
1097
|
+
|
|
1132
1098
|
1. With the introduction of Ember Octane and glimmer components, should we use any of their features? How backward compatible should this be?
|
|
1133
1099
|
|
|
1134
1100
|
2. Will an external animation library be of high value to us? If so, will the extra overhead be problematic?
|