@brightspace-ui/core 2.77.0 → 2.78.1

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 (37) hide show
  1. package/components/button/button-icon.js +1 -0
  2. package/components/collapsible-panel/README.md +28 -15
  3. package/components/list/README.md +67 -0
  4. package/components/list/demo/demo-list-nested-lazy-load.js +133 -0
  5. package/components/list/demo/demo-list-nested.js +279 -0
  6. package/components/list/demo/list-demo-scenarios.js +321 -0
  7. package/components/list/demo/list-drag-and-drop.html +2 -2
  8. package/components/list/demo/list-expand-collapse.html +134 -0
  9. package/components/list/list-item-checkbox-mixin.js +2 -8
  10. package/components/list/list-item-drag-drop-mixin.js +78 -14
  11. package/components/list/list-item-drag-image.js +5 -3
  12. package/components/list/list-item-expand-collapse-mixin.js +168 -0
  13. package/components/list/list-item-generic-layout.js +21 -12
  14. package/components/list/list-item-mixin.js +88 -11
  15. package/components/list/list.js +45 -9
  16. package/components/selection/selection-summary.js +43 -12
  17. package/custom-elements.json +386 -194
  18. package/lang/ar.js +1 -0
  19. package/lang/cy.js +1 -0
  20. package/lang/da.js +1 -0
  21. package/lang/de.js +1 -0
  22. package/lang/en.js +1 -0
  23. package/lang/es-es.js +1 -0
  24. package/lang/es.js +1 -0
  25. package/lang/fr-fr.js +1 -0
  26. package/lang/fr.js +1 -0
  27. package/lang/hi.js +1 -0
  28. package/lang/ja.js +1 -0
  29. package/lang/ko.js +1 -0
  30. package/lang/nl.js +1 -0
  31. package/lang/pt.js +1 -0
  32. package/lang/sv.js +1 -0
  33. package/lang/tr.js +1 -0
  34. package/lang/zh-cn.js +1 -0
  35. package/lang/zh-tw.js +1 -0
  36. package/package.json +1 -1
  37. package/components/list/demo/list-drag-and-drop.js +0 -181
@@ -0,0 +1,321 @@
1
+ export const listDemos = {
2
+ imgPrimaryAndSupporting: [{
3
+ key: '1',
4
+ primaryText: 'Introductory Earth Sciences',
5
+ supportingText: 'This course explores the geological processes of the Earth\'s interior and surface. These include volcanism, earthquakes, mountain building, glaciation and weathering.',
6
+ imgSrc: 'https://s.brightspace.com/course-images/images/63b162ab-b582-4bf9-8c1d-1dad04714121/tile-high-density-max-size.jpg',
7
+ dropNested: true,
8
+ items: [{
9
+ key: '1-1',
10
+ primaryText: 'Glaciation',
11
+ supportingText: 'Nesting Allowed',
12
+ imgSrc: 'https://s.brightspace.com/course-images/images/bf648978-6637-4fdc-815b-81572c436c0e/tile-high-density-max-size.jpg',
13
+ dropNested: true,
14
+ items: []
15
+ }, {
16
+ key: '1-2',
17
+ primaryText: 'Weathering',
18
+ supportingText: 'Nesting Allowed',
19
+ imgSrc: 'https://s.brightspace.com/course-images/images/50f91ba6-7c25-482a-bd71-1c4b7c8d2154/tile-high-density-min-size.jpg',
20
+ dropNested: true,
21
+ items: []
22
+ }, {
23
+ key: '1-3',
24
+ primaryText: 'Volcanism',
25
+ supportingText: 'Nesting Allowed',
26
+ imgSrc: 'https://s.brightspace.com/course-images/images/5eb2371d-6099-4c8d-8aad-075f357012a2/tile-high-density-min-size.jpg',
27
+ dropNested: true,
28
+ items: []
29
+ }]
30
+ }, {
31
+ key: '2',
32
+ primaryText: 'Applied Wetland Science',
33
+ supportingText: 'Advanced concepts on wetland ecosystems in the context of regional and global earth systems processes such as carbon and nitrogen cycling and climate change, applications of wetland paleoecology, use of isotopes and other geochemical tools in wetland science, and wetland engineering in landscape rehabilitation and ecotechnology.',
34
+ imgSrc: 'https://s.brightspace.com/course-images/images/38e839b1-37fa-470c-8830-b189ce4ae134/tile-high-density-max-size.jpg',
35
+ items: [{
36
+ key: '-1',
37
+ primaryText: 'Carbon & Nitrogen Cycling',
38
+ supportingText: 'Nesting Allowed',
39
+ imgSrc: 'https://s.brightspace.com/course-images/images/623b420b-a305-4762-8af8-598f0e72e956/tile-high-density-min-size.jpg',
40
+ dropNested: true,
41
+ items: []
42
+ }, {
43
+ key: '2-2',
44
+ primaryText: 'Wetland Engineering',
45
+ supportingText: 'Nesting Allowed',
46
+ imgSrc: 'https://s.brightspace.com/course-images/images/26102577-8f2a-4e24-84b5-19d76decbc7a/tile-high-density-min-size.jpg',
47
+ dropNested: true,
48
+ items: []
49
+ }]
50
+ }],
51
+ primaryAndSupportingText: [{
52
+ key: '1',
53
+ primaryText: 'Introductory Earth Sciences',
54
+ supportingText: 'This course explores the geological processes of the Earth\'s interior and surface. These include volcanism, earthquakes, mountain building, glaciation and weathering.',
55
+ dropNested: true,
56
+ items: [{
57
+ key: '1-1',
58
+ primaryText: 'Glaciation',
59
+ supportingText: 'Nesting Allowed',
60
+ dropNested: true,
61
+ items: []
62
+ }, {
63
+ key: '1-2',
64
+ primaryText: 'Weathering',
65
+ supportingText: 'Nesting Allowed',
66
+ dropNested: true,
67
+ items: []
68
+ }, {
69
+ key: '1-3',
70
+ primaryText: 'Volcanism',
71
+ supportingText: 'Nesting Allowed',
72
+ dropNested: true,
73
+ items: []
74
+ }]
75
+ }, {
76
+ key: '2',
77
+ primaryText: 'Applied Wetland Science',
78
+ supportingText: 'Advanced concepts on wetland ecosystems in the context of regional and global earth systems processes such as carbon and nitrogen cycling and climate change, applications of wetland paleoecology, use of isotopes and other geochemical tools in wetland science, and wetland engineering in landscape rehabilitation and ecotechnology.',
79
+ items: [{
80
+ key: '2-1',
81
+ primaryText: 'Carbon & Nitrogen Cycling',
82
+ supportingText: 'Nesting Allowed',
83
+ dropNested: true,
84
+ items: []
85
+ }, {
86
+ key: '2-2',
87
+ primaryText: 'Wetland Engineering',
88
+ supportingText: 'Nesting Allowed',
89
+ dropNested: true,
90
+ items: []
91
+ }]
92
+ }],
93
+ primaryTextOnly: [{
94
+ key: '1',
95
+ primaryText: 'Introductory Earth Sciences',
96
+ dropNested: true,
97
+ items: [{
98
+ key: '1-1',
99
+ primaryText: 'Glaciation',
100
+ dropNested: true,
101
+ items: []
102
+ }, {
103
+ key: '1-2',
104
+ primaryText: 'Weathering',
105
+ dropNested: true,
106
+ items: []
107
+ }, {
108
+ key: '1-3',
109
+ primaryText: 'Volcanism',
110
+ dropNested: true,
111
+ items: []
112
+ }]
113
+ }, {
114
+ key: '2',
115
+ primaryText: 'Applied Wetland Science',
116
+ items: [{
117
+ key: '2-1',
118
+ primaryText: 'Carbon & Nitrogen Cycling',
119
+ dropNested: true,
120
+ items: []
121
+ }, {
122
+ key: '2-2',
123
+ primaryText: 'Wetland Engineering',
124
+ dropNested: true,
125
+ items: []
126
+ }]
127
+ }],
128
+ primaryTextOnlyDeepNesting: [{
129
+ key: '1',
130
+ primaryText: 'Item 1 - Deeply Nested',
131
+ dropNested: true,
132
+ items: [{
133
+ key: '1-1',
134
+ primaryText: 'Glaciation',
135
+ dropNested: true,
136
+ items: [{
137
+ key: '1-1-1',
138
+ primaryText: 'Ice',
139
+ dropNested: true,
140
+ items: [{
141
+ key: '1-1-1-1',
142
+ primaryText: 'Cold',
143
+ dropNested: true,
144
+ items: [{
145
+ key: '1-1-1-1-1',
146
+ primaryText: 'Winter',
147
+ dropNested: true,
148
+ items: [{
149
+ key: '1-1-1-1-1-1',
150
+ primaryText: 'Canada',
151
+ dropNested: true,
152
+ items: [{
153
+ key: '1-1-1-1-1-1-1',
154
+ primaryText: 'Moose',
155
+ dropNested: true,
156
+ items: []
157
+ }]
158
+ }]
159
+ }]
160
+ }]
161
+ }, {
162
+ key: '1-1-2',
163
+ primaryText: 'Snow',
164
+ dropNested: true,
165
+ items: []
166
+ }]
167
+ }, {
168
+ key: '1-2',
169
+ primaryText: 'Weathering',
170
+ dropNested: true,
171
+ items: []
172
+ }, {
173
+ key: '1-3',
174
+ primaryText: 'Volcanism',
175
+ dropNested: true,
176
+ items: []
177
+ }]
178
+ }, {
179
+ key: '2',
180
+ primaryText: 'Item 2',
181
+ items: [{
182
+ key: '2-1',
183
+ primaryText: '2-1',
184
+ dropNested: true,
185
+ items: []
186
+ }, {
187
+ key: '2-2',
188
+ primaryText: '2-2',
189
+ dropNested: true,
190
+ items: []
191
+ }]
192
+ }, {
193
+ key: '3',
194
+ primaryText: 'Item 3',
195
+ items: [{
196
+ key: '3-1',
197
+ primaryText: '3-1',
198
+ dropNested: true,
199
+ items: []
200
+ }, {
201
+ key: '3-2',
202
+ primaryText: '3-2',
203
+ dropNested: true,
204
+ items: []
205
+ }]
206
+ }, {
207
+ key: '4',
208
+ primaryText: 'Item 4',
209
+ items: [{
210
+ key: '4-1',
211
+ primaryText: '4-1',
212
+ dropNested: true,
213
+ items: []
214
+ }, {
215
+ key: '4-2',
216
+ primaryText: '4-2',
217
+ dropNested: true,
218
+ items: []
219
+ }]
220
+ }, {
221
+ key: '5',
222
+ primaryText: 'Item 5',
223
+ items: [{
224
+ key: '5-1',
225
+ primaryText: '5-1',
226
+ dropNested: true,
227
+ items: []
228
+ }, {
229
+ key: '5-2',
230
+ primaryText: '5-2',
231
+ dropNested: true,
232
+ items: []
233
+ }]
234
+ }, {
235
+ key: '6',
236
+ primaryText: 'Item 6',
237
+ items:[{
238
+ key: '6-1',
239
+ primaryText: '6-1',
240
+ dropNested: true,
241
+ items: []
242
+ }, {
243
+ key: '6-2',
244
+ primaryText: '6-2',
245
+ dropNested: true,
246
+ items: []
247
+ }]
248
+ }, {
249
+ key: '7',
250
+ primaryText: 'Item 7',
251
+ items: [{
252
+ key: '7-1',
253
+ primaryText: '7-1',
254
+ dropNested: true,
255
+ items: []
256
+ }, {
257
+ key: '7-2',
258
+ primaryText: '7-2',
259
+ dropNested: true,
260
+ items: []
261
+ }]
262
+ }, {
263
+ key: '8',
264
+ primaryText: 'Item 8',
265
+ items: [{
266
+ key: '8-1',
267
+ primaryText: '8-1',
268
+ dropNested: true,
269
+ items: []
270
+ }, {
271
+ key: '8-2',
272
+ primaryText: '8-2',
273
+ dropNested: true,
274
+ items: []
275
+ }]
276
+ }, {
277
+ key: '9',
278
+ primaryText: 'Item 9',
279
+ items: [{
280
+ key: '9-1',
281
+ primaryText: '9-1',
282
+ dropNested: true,
283
+ items: []
284
+ }, {
285
+ key: '9-2',
286
+ primaryText: '9-2',
287
+ dropNested: true,
288
+ items: []
289
+ }]
290
+ }, {
291
+ key: '10',
292
+ primaryText: 'Item 10',
293
+ items: [{
294
+ key: '10-1',
295
+ primaryText: '10-1',
296
+ dropNested: true,
297
+ items: []
298
+ }, {
299
+ key: '10-2',
300
+ primaryText: '10-2',
301
+ dropNested: true,
302
+ items: []
303
+ }]
304
+ }],
305
+ oneChild: [{
306
+ key: '1',
307
+ primaryText: 'Introductory Earth Sciences',
308
+ dropNested: true,
309
+ items: [{
310
+ key: '1-1',
311
+ primaryText: 'Glaciation',
312
+ dropNested: true,
313
+ items: []
314
+ }]
315
+ }, {
316
+ key: '2',
317
+ primaryText: 'Applied Wetland Science',
318
+ dropNested: true,
319
+ items: []
320
+ }]
321
+ };
@@ -7,7 +7,7 @@
7
7
  <script type="module">
8
8
  import '../../demo/demo-page.js';
9
9
  import './list-drag-and-drop-position.js';
10
- import './list-drag-and-drop.js';
10
+ import './demo-list-nested.js';
11
11
  </script>
12
12
  </head>
13
13
  <body unresolved>
@@ -26,7 +26,7 @@
26
26
 
27
27
  <d2l-demo-snippet>
28
28
  <template>
29
- <d2l-demo-list-drag-and-drop></d2l-demo-list-drag-and-drop>
29
+ <d2l-demo-list-nested demo-item-key="imgPrimaryAndSupporting" draggable selectable disable-expand-feature></d2l-demo-list-nested>
30
30
  </template>
31
31
  </d2l-demo-snippet>
32
32
 
@@ -0,0 +1,134 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
5
+ <meta charset="UTF-8">
6
+ <link rel="stylesheet" href="../../demo/styles.css" type="text/css">
7
+ <script type="module">
8
+ import '../../demo/demo-page.js';
9
+ import './demo-list-nested.js';
10
+ import './demo-list-nested-lazy-load.js';
11
+ </script>
12
+ </head>
13
+ <body unresolved>
14
+
15
+ <d2l-demo-page page-title="d2l-list (with expand/collapse)">
16
+ <h2>Draggable & Selectable (with grid)</h2>
17
+ <d2l-demo-snippet>
18
+ <template>
19
+ <d2l-demo-list-nested
20
+ demo-item-key="imgPrimaryAndSupporting"
21
+ draggable
22
+ selectable
23
+ expandable
24
+ expanded
25
+ include-action-href
26
+ include-secondary-actions
27
+ include-list-header></d2l-demo-list-nested>
28
+ </template>
29
+ </d2l-demo-snippet>
30
+
31
+ <d2l-demo-snippet>
32
+ <template>
33
+ <d2l-demo-list-nested demo-item-key="primaryAndSupportingText" draggable selectable expandable></d2l-demo-list-nested>
34
+ </template>
35
+ </d2l-demo-snippet>
36
+
37
+ <d2l-demo-snippet>
38
+ <template>
39
+ <d2l-demo-list-nested demo-item-key="primaryTextOnly" draggable selectable expandable></d2l-demo-list-nested>
40
+ </template>
41
+ </d2l-demo-snippet>
42
+
43
+ <h2>Draggable & Selectable (without grid)</h2>
44
+ <d2l-demo-snippet>
45
+ <template>
46
+ <d2l-demo-list-nested
47
+ demo-item-key="imgPrimaryAndSupporting"
48
+ draggable
49
+ selectable
50
+ expandable
51
+ expanded
52
+ include-secondary-actions
53
+ include-list-header
54
+ disable-list-grid></d2l-demo-list-nested>
55
+ </template>
56
+ </d2l-demo-snippet>
57
+
58
+ <h2>Selectable</h2>
59
+ <d2l-demo-snippet>
60
+ <template>
61
+ <d2l-demo-list-nested demo-item-key="primaryAndSupportingText" selectable expandable expanded></d2l-demo-list-nested>
62
+ </template>
63
+ </d2l-demo-snippet>
64
+
65
+ <h2>Draggable</h2>
66
+ <d2l-demo-snippet>
67
+ <template>
68
+ <d2l-demo-list-nested demo-item-key="primaryAndSupportingText" draggable expandable></d2l-demo-list-nested>
69
+ </template>
70
+ </d2l-demo-snippet>
71
+
72
+ <h2>Expandable</h2>
73
+ <d2l-demo-snippet>
74
+ <template>
75
+ <d2l-demo-list-nested demo-item-key="primaryTextOnlyDeepNesting" expandable></d2l-demo-list-nested>
76
+ </template>
77
+ </d2l-demo-snippet>
78
+
79
+ <h2>Button list item</h2>
80
+ <d2l-demo-snippet>
81
+ <template>
82
+ <d2l-demo-list-nested
83
+ demo-item-key="imgPrimaryAndSupporting"
84
+ draggable
85
+ selectable
86
+ expandable
87
+ include-secondary-actions
88
+ include-list-header
89
+ use-button-item></d2l-demo-list-nested>
90
+ </template>
91
+ </d2l-demo-snippet>
92
+
93
+ <h2>Load More</h2>
94
+ <d2l-demo-snippet>
95
+ <template>
96
+ <d2l-demo-list-nested demo-item-key="primaryTextOnlyDeepNesting" expandable expanded show-load-more></d2l-demo-list-nested>
97
+ </template>
98
+ </d2l-demo-snippet>
99
+
100
+ <h2>One Child</h2>
101
+ <d2l-demo-snippet>
102
+ <template>
103
+ <d2l-demo-list-nested demo-item-key="oneChild" expandable include-action-href></d2l-demo-list-nested>
104
+ </template>
105
+ </d2l-demo-snippet>
106
+
107
+ <h2>No Primary Action</h2>
108
+ <d2l-demo-snippet>
109
+ <template>
110
+ <d2l-demo-list-nested
111
+ demo-item-key="imgPrimaryAndSupporting"
112
+ draggable
113
+ selectable
114
+ expandable
115
+ no-primary-action></d2l-demo-list-nested>
116
+ </template>
117
+ </d2l-demo-snippet>
118
+
119
+ <h2>Lazy Loaded Children</h2>
120
+ <d2l-demo-snippet>
121
+ <template>
122
+ <d2l-demo-list-nested-lazy-load></d2l-demo-list-nested-lazy-load>
123
+ </template>
124
+ </d2l-demo-snippet>
125
+ </d2l-demo-page>
126
+
127
+ <script>
128
+ document.body.addEventListener('d2l-list-items-move', e => {
129
+ console.log('d2l-list-items-move', e.detail);
130
+ });
131
+ </script>
132
+
133
+ </body>
134
+ </html>
@@ -2,14 +2,10 @@ import '../selection/selection-input.js';
2
2
  import { css, html, nothing } from 'lit';
3
3
  import { classMap } from 'lit/directives/class-map.js';
4
4
  import { getUniqueId } from '../../helpers/uniqueId.js';
5
- import { LabelledMixin } from '../../mixins/labelled-mixin.js';
6
5
  import { SelectionInfo } from '../selection/selection-mixin.js';
7
6
  import { SkeletonMixin } from '../skeleton/skeleton-mixin.js';
8
7
 
9
- /**
10
- * @property label - The hidden label for the checkbox if selectable
11
- */
12
- export const ListItemCheckboxMixin = superclass => class extends SkeletonMixin(LabelledMixin(superclass)) {
8
+ export const ListItemCheckboxMixin = superclass => class extends SkeletonMixin(superclass) {
13
9
 
14
10
  static get properties() {
15
11
  return {
@@ -83,8 +79,6 @@ export const ListItemCheckboxMixin = superclass => class extends SkeletonMixin(L
83
79
  super.connectedCallback();
84
80
  if (this.selectable) {
85
81
  if (!this.key) console.warn('ListItemCheckboxMixin requires a key.');
86
- } else {
87
- this.labelRequired = false;
88
82
  }
89
83
  if (!this.key) this.setSelected(undefined, true);
90
84
  }
@@ -130,7 +124,7 @@ export const ListItemCheckboxMixin = superclass => class extends SkeletonMixin(L
130
124
  }
131
125
  }
132
126
 
133
- _onNestedSlotChange() {
127
+ _onNestedSlotChangeCheckboxMixin() {
134
128
  this._updateNestedSelectionProvider();
135
129
  }
136
130
 
@@ -363,6 +363,7 @@ export const ListItemDragDropMixin = superclass => class extends superclass {
363
363
 
364
364
  firstUpdated(changedProperties) {
365
365
  this.addEventListener('dragenter', this._onHostDragEnter.bind(this));
366
+ this.addEventListener('dragleave', this._onHostDragLeave.bind(this));
366
367
  super.firstUpdated(changedProperties);
367
368
  }
368
369
 
@@ -395,6 +396,17 @@ export const ListItemDragDropMixin = superclass => class extends superclass {
395
396
  }));
396
397
  }
397
398
 
399
+ _dispatchMoveAroundCollapsedItem(listItem, moveAbove) {
400
+ const parentListItem = this._getParentList();
401
+ if (parentListItem) {
402
+ const parentItems = parentListItem.getItems();
403
+ const nextItemIdex = parentItems.indexOf(listItem);
404
+ this._dispatchListItemsMove([this], parentItems[nextItemIdex], moveAbove ? moveLocations.above : moveLocations.below, true);
405
+ } else {
406
+ this._dispatchMoveRootItem(moveAbove);
407
+ }
408
+ }
409
+
398
410
  _dispatchMoveListItemFirst(moveToRoot) {
399
411
  const list = (moveToRoot ? this._getRootList() : findComposedAncestor(this, node => node.tagName === 'D2L-LIST'));
400
412
  const items = list.getItems();
@@ -410,42 +422,54 @@ export const ListItemDragDropMixin = superclass => class extends superclass {
410
422
  _dispatchMoveListItemNest() {
411
423
  const listItem = this._getPreviousListItemSibling();
412
424
  if (listItem) {
425
+ this._expandListItemOnKeyboardMove(listItem);
413
426
  this._dispatchListItemsMove([this], listItem, moveLocations.nest, true);
414
427
  }
415
428
  }
416
429
 
417
430
  _dispatchMoveListItemNext() {
418
- const listItem = this._getNextListItemSibling();
419
- if (listItem) {
420
- const nestedList = listItem._getNestedList();
431
+ const nextListItemSibling = this._getNextListItemSibling();
432
+ // if next sibling is collapsed - move item below it
433
+ if (nextListItemSibling && nextListItemSibling.expandable && !nextListItemSibling.expanded) {
434
+ this._dispatchMoveAroundCollapsedItem(nextListItemSibling, false);
435
+ } else if (nextListItemSibling) {
436
+ this._expandListItemOnKeyboardMove(nextListItemSibling);
437
+ const nestedList = nextListItemSibling._getNestedList();
421
438
  const items = (nestedList ? nestedList.getItems() : []);
422
439
  if (items.length > 0) {
423
440
  this._dispatchListItemsMove([this], items[0], moveLocations.above, true);
424
441
  } else {
425
- this._dispatchListItemsMove([this], listItem, moveLocations.below, true);
442
+ this._dispatchListItemsMove([this], nextListItemSibling, moveLocations.below, true);
426
443
  }
427
444
  } else {
428
445
  const parentListItem = this._getParentListItem();
429
446
  if (parentListItem) {
430
447
  this._dispatchListItemsMove([this], parentListItem, moveLocations.below, true);
448
+ } else {
449
+ this._dispatchMoveRootItem(false);
431
450
  }
432
451
  }
433
452
  }
434
453
 
435
454
  _dispatchMoveListItemPrevious() {
436
- const listItem = this._getPreviousListItemSibling();
437
- if (listItem) {
438
- const nestedList = listItem._getNestedList();
455
+ const previousListItemSibling = this._getPreviousListItemSibling();
456
+ // if previous sibling is collapsed - move item above it
457
+ if (previousListItemSibling && previousListItemSibling.expandable && !previousListItemSibling.expanded) {
458
+ this._dispatchMoveAroundCollapsedItem(previousListItemSibling, true);
459
+ } else if (previousListItemSibling) {
460
+ const nestedList = previousListItemSibling._getNestedList();
439
461
  const items = (nestedList ? nestedList.getItems() : []);
440
462
  if (items.length > 0) {
441
463
  this._dispatchListItemsMove([this], items[items.length - 1], moveLocations.below, true);
442
464
  } else {
443
- this._dispatchListItemsMove([this], listItem, moveLocations.above, true);
465
+ this._dispatchListItemsMove([this], previousListItemSibling, moveLocations.above, true);
444
466
  }
445
467
  } else {
446
468
  const parentListItem = this._getParentListItem();
447
469
  if (parentListItem) {
448
470
  this._dispatchListItemsMove([this], parentListItem, moveLocations.above, true);
471
+ } else {
472
+ this._dispatchMoveRootItem(true);
449
473
  }
450
474
  }
451
475
  }
@@ -453,10 +477,28 @@ export const ListItemDragDropMixin = superclass => class extends superclass {
453
477
  _dispatchMoveListItemUnnest() {
454
478
  const listItem = this._getParentListItem();
455
479
  if (listItem) {
480
+ this._expandListItemOnKeyboardMove(listItem);
456
481
  this._dispatchListItemsMove([this], listItem, moveLocations.below, true);
457
482
  }
458
483
  }
459
484
 
485
+ _dispatchMoveRootItem(moveAbove) {
486
+ const rootList = this._getRootList();
487
+ const items = rootList.getItems();
488
+ const currentIndex = items.indexOf(this);
489
+ if (moveAbove && currentIndex !== 0) {
490
+ this._dispatchListItemsMove([this], items[currentIndex - 1], moveLocations.above, true);
491
+ } else if (!moveAbove && currentIndex !== items.length - 1) {
492
+ this._dispatchListItemsMove([this], items[currentIndex + 1], moveLocations.below, true);
493
+ }
494
+ }
495
+
496
+ _expandListItemOnKeyboardMove(listItem) {
497
+ if (this._keyboardActive && listItem.expandable && !listItem.expanded) {
498
+ listItem._toggleExpandCollapse();
499
+ }
500
+ }
501
+
460
502
  _findListItemFromCoordinates(x, y) {
461
503
  const listNode = findComposedAncestor(this.parentNode, (node) => node && node.tagName === 'D2L-LIST');
462
504
  return listNode.shadowRoot.elementFromPoint(x, y);
@@ -557,17 +599,34 @@ export const ListItemDragDropMixin = superclass => class extends superclass {
557
599
  e.dataTransfer.setData('text/plain', `${this.dropText}`);
558
600
  }
559
601
 
560
- const rootList = this._getRootList(this);
561
- const selectionInfo = rootList.getSelectionInfo(rootList.dragMultiple);
562
-
563
- if (rootList.dragMultiple && selectionInfo.keys.length > 1) {
602
+ const getDragImage = (count, includePlusSign) => {
564
603
  let dragImage = this.shadowRoot.querySelector('d2l-list-item-drag-image');
565
604
  if (!dragImage) {
566
605
  dragImage = document.createElement('d2l-list-item-drag-image');
567
606
  this.shadowRoot.appendChild(dragImage);
568
607
  }
569
- dragImage.count = selectionInfo.keys.length;
570
- e.dataTransfer.setDragImage(dragImage, 24, 26);
608
+ dragImage.count = count;
609
+ dragImage.includePlusSign = includePlusSign;
610
+ return dragImage;
611
+ };
612
+
613
+ const rootList = this._getRootList(this);
614
+ const selectionInfo = rootList.getSelectionInfo(rootList.dragMultiple);
615
+ if (rootList.dragMultiple && selectionInfo.keys.length > 1) {
616
+ const lazyLoadListItems = this._getFlattenedListItems().lazyLoadListItems;
617
+ let includePlus = false;
618
+ if (lazyLoadListItems.size > 0) {
619
+ for (const selectedItemKey of selectionInfo.keys) {
620
+ if (lazyLoadListItems.has(selectedItemKey)) {
621
+ includePlus = true;
622
+ break;
623
+ }
624
+ }
625
+ }
626
+ e.dataTransfer.setDragImage(getDragImage(selectionInfo.keys.length, includePlus), 24, 26);
627
+ } else if (rootList.dragMultiple && this.expandable) {
628
+ const flattenedListItems = this._getFlattenedListItems(this);
629
+ e.dataTransfer.setDragImage(getDragImage(flattenedListItems.listItems.size, flattenedListItems.lazyLoadListItems.size > 0), 24, 26);
571
630
  } else {
572
631
  if (this.shadowRoot) {
573
632
  const nodeImage = this.shadowRoot.querySelector('.d2l-list-item-drag-image') || this;
@@ -697,6 +756,10 @@ export const ListItemDragDropMixin = superclass => class extends superclass {
697
756
  e.dataTransfer.dropEffect = 'move';
698
757
  }
699
758
 
759
+ _onHostDragLeave() {
760
+ this._draggingOver = false;
761
+ }
762
+
700
763
  _onTouchCancel() {
701
764
  if (this._touchTimeoutId) clearTimeout(this._touchTimeoutId);
702
765
  this._touchStarted = false;
@@ -735,6 +798,7 @@ export const ListItemDragDropMixin = superclass => class extends superclass {
735
798
  if (!listItem) return;
736
799
  // simulate host dragenter
737
800
  if (listItem !== this && this._currentTouchListItem !== listItem) {
801
+ this._currentTouchListItem.dispatchEvent(createDragEvent('dragleave'));
738
802
  listItem.dispatchEvent(createDragEvent('dragenter'));
739
803
  this._currentTouchListItem = listItem;
740
804
  }