@brightspace-ui/core 2.77.0 → 2.78.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/components/button/button-icon.js +1 -0
- package/components/list/README.md +67 -0
- package/components/list/demo/demo-list-nested-lazy-load.js +133 -0
- package/components/list/demo/demo-list-nested.js +279 -0
- package/components/list/demo/list-demo-scenarios.js +321 -0
- package/components/list/demo/list-drag-and-drop.html +2 -2
- package/components/list/demo/list-expand-collapse.html +134 -0
- package/components/list/list-item-checkbox-mixin.js +2 -8
- package/components/list/list-item-drag-drop-mixin.js +78 -14
- package/components/list/list-item-drag-image.js +5 -3
- package/components/list/list-item-expand-collapse-mixin.js +168 -0
- package/components/list/list-item-generic-layout.js +21 -12
- package/components/list/list-item-mixin.js +88 -11
- package/components/list/list.js +45 -9
- package/components/selection/selection-summary.js +43 -12
- package/custom-elements.json +386 -194
- package/lang/ar.js +1 -0
- package/lang/cy.js +1 -0
- package/lang/da.js +1 -0
- package/lang/de.js +1 -0
- package/lang/en.js +1 -0
- package/lang/es-es.js +1 -0
- package/lang/es.js +1 -0
- package/lang/fr-fr.js +1 -0
- package/lang/fr.js +1 -0
- package/lang/hi.js +1 -0
- package/lang/ja.js +1 -0
- package/lang/ko.js +1 -0
- package/lang/nl.js +1 -0
- package/lang/pt.js +1 -0
- package/lang/sv.js +1 -0
- package/lang/tr.js +1 -0
- package/lang/zh-cn.js +1 -0
- package/lang/zh-tw.js +1 -0
- package/package.json +1 -1
- package/components/list/demo/list-drag-and-drop.js +0 -181
|
@@ -3,17 +3,19 @@ import '../inputs/input-checkbox.js';
|
|
|
3
3
|
import { css, html, LitElement } from 'lit';
|
|
4
4
|
import { bodySmallStyles } from '../typography/styles.js';
|
|
5
5
|
import { formatNumber } from '@brightspace-ui/intl/lib/number.js';
|
|
6
|
+
import { LocalizeCoreElement } from '../../helpers/localize-core-element.js';
|
|
6
7
|
import { RtlMixin } from '../../mixins/rtl-mixin.js';
|
|
7
8
|
import { SkeletonMixin } from '../skeleton/skeleton-mixin.js';
|
|
8
9
|
|
|
9
|
-
class ListItemDragImage extends SkeletonMixin(RtlMixin(LitElement)) {
|
|
10
|
+
class ListItemDragImage extends LocalizeCoreElement(SkeletonMixin(RtlMixin(LitElement))) {
|
|
10
11
|
|
|
11
12
|
static get properties() {
|
|
12
13
|
return {
|
|
13
14
|
/**
|
|
14
15
|
* @ignore
|
|
15
16
|
*/
|
|
16
|
-
count: { type: Number }
|
|
17
|
+
count: { type: Number },
|
|
18
|
+
includePlusSign: { type: Boolean, attribute: 'include-plus-sign' }
|
|
17
19
|
};
|
|
18
20
|
}
|
|
19
21
|
|
|
@@ -103,7 +105,7 @@ class ListItemDragImage extends SkeletonMixin(RtlMixin(LitElement)) {
|
|
|
103
105
|
render() {
|
|
104
106
|
return html`
|
|
105
107
|
<div class="first">
|
|
106
|
-
<div class="count d2l-body-small">${formatNumber(this.count)}</div>
|
|
108
|
+
<div class="count d2l-body-small">${this.includePlusSign ? this.localize('components.count-badge.plus', { number: this.count }) : formatNumber(this.count)}</div>
|
|
107
109
|
<svg width="18" height="18" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 18 18">
|
|
108
110
|
<path fill="#494c4e" d="M8 16v1c0 .5-.4 1-1 1H6c-.6 0-1-.5-1-1v-1c0-.6.4-1 1-1h1c.6 0 1 .4 1 1M13 16v1c0 .5-.4 1-1 1h-1c-.6 0-1-.5-1-1v-1c0-.6.4-1 1-1h1c.6 0 1 .4 1 1M8 11v1c0 .6-.4 1-1 1H6c-.6 0-1-.4-1-1v-1c0-.6.4-1 1-1h1c.6 0 1 .4 1 1M13 11v1c0 .6-.4 1-1 1h-1c-.6 0-1-.4-1-1v-1c0-.6.4-1 1-1h1c.6 0 1 .4 1 1M8 6v1c0 .6-.4 1-1 1H6c-.6 0-1-.4-1-1V6c0-.6.4-1 1-1h1c.6 0 1 .4 1 1M13 6v1c0 .6-.4 1-1 1h-1c-.6 0-1-.4-1-1V6c0-.6.4-1 1-1h1c.6 0 1 .4 1 1M8 1v1c0 .6-.4 1-1 1H6c-.6 0-1-.4-1-1V1c0-.5.4-1 1-1h1c.6 0 1 .5 1 1M13 1v1c0 .6-.4 1-1 1h-1c-.6 0-1-.4-1-1V1c0-.5.4-1 1-1h1c.6 0 1 .5 1 1"/>
|
|
109
111
|
</svg>
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
import '../button/button-icon.js';
|
|
2
|
+
import '../loading-spinner/loading-spinner.js';
|
|
3
|
+
import { css, html, nothing } from 'lit';
|
|
4
|
+
import { EventSubscriberController } from '../../controllers/subscriber/subscriberControllers.js';
|
|
5
|
+
|
|
6
|
+
const dragIntervalDelay = 100;
|
|
7
|
+
const dragHoverDropTime = 1000;
|
|
8
|
+
|
|
9
|
+
export const ListItemExpandCollapseMixin = superclass => class extends superclass {
|
|
10
|
+
|
|
11
|
+
static get properties() {
|
|
12
|
+
return {
|
|
13
|
+
/**
|
|
14
|
+
* Whether to show the expand collapse toggle
|
|
15
|
+
* @type {boolean}
|
|
16
|
+
*/
|
|
17
|
+
expandable: { type: Boolean },
|
|
18
|
+
/**
|
|
19
|
+
* Default state for expand collapse toggle - if not set, collapsed will be the default state
|
|
20
|
+
* @type {boolean}
|
|
21
|
+
*/
|
|
22
|
+
expanded: { type: Boolean, reflect: true },
|
|
23
|
+
_siblingHasNestedItems: { state: true },
|
|
24
|
+
_renderExpandCollapseSlot: { type: Boolean, reflect: true, attribute: '_render-expand-collapse-slot' },
|
|
25
|
+
_showNestedLoadingSpinner: { state: true }
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
static get styles() {
|
|
30
|
+
const styles = [ css`
|
|
31
|
+
:host {
|
|
32
|
+
--d2l-expand-collapse-slot-transition-duration: 0.3s;
|
|
33
|
+
}
|
|
34
|
+
.d2l-list-expand-collapse {
|
|
35
|
+
width: 0;
|
|
36
|
+
}
|
|
37
|
+
:host([dir="rtl"][_render-expand-collapse-slot]) .d2l-list-expand-collapse {
|
|
38
|
+
padding: 0.4rem 0 0 0.3rem;
|
|
39
|
+
}
|
|
40
|
+
.d2l-list-expand-collapse d2l-button-icon {
|
|
41
|
+
--d2l-button-icon-min-height: 1.2rem;
|
|
42
|
+
--d2l-button-icon-min-width: 1.2rem;
|
|
43
|
+
}
|
|
44
|
+
.d2l-list-expand-collapse:hover d2l-button-icon {
|
|
45
|
+
background-color: var(--d2l-button-icon-background-color-hover);
|
|
46
|
+
border-radius: var(--d2l-button-icon-border-radius);
|
|
47
|
+
}
|
|
48
|
+
:host([_render-expand-collapse-slot]) .d2l-list-expand-collapse {
|
|
49
|
+
padding: 0.4rem 0.3rem 0 0;
|
|
50
|
+
width: 1.2rem;
|
|
51
|
+
}
|
|
52
|
+
.d2l-list-expand-collapse-action {
|
|
53
|
+
cursor: pointer;
|
|
54
|
+
display: block;
|
|
55
|
+
height: 100%;
|
|
56
|
+
}
|
|
57
|
+
.d2l-list-nested-loading {
|
|
58
|
+
display: flex;
|
|
59
|
+
justify-content: center;
|
|
60
|
+
padding: 0.4rem;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
@media (prefers-reduced-motion: no-preference) {
|
|
64
|
+
.d2l-list-expand-collapse {
|
|
65
|
+
transition: width var(--d2l-expand-collapse-slot-transition-duration) cubic-bezier(0, 0.7, 0.5, 1);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
` ];
|
|
69
|
+
|
|
70
|
+
super.styles && styles.unshift(super.styles);
|
|
71
|
+
return styles;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
constructor() {
|
|
75
|
+
super();
|
|
76
|
+
this._siblingHasNestedItems = false;
|
|
77
|
+
this._renderExpandCollapseSlot = false;
|
|
78
|
+
this._showNestedLoadingSpinner = false;
|
|
79
|
+
this._parentChildUpdateSubscription = new EventSubscriberController(this, {}, { eventName: 'd2l-list-child-status' });
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
connectedCallback() {
|
|
83
|
+
super.connectedCallback();
|
|
84
|
+
// mixin requires key for events
|
|
85
|
+
if (!this.key && this.expandable) {
|
|
86
|
+
console.warn('ListItemExpandCollapseMixin requires a key.');
|
|
87
|
+
this.expandable = false;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
updated(changedProperties) {
|
|
92
|
+
if (changedProperties.has('_siblingHasNestedItems') || changedProperties.has('expandable')) {
|
|
93
|
+
this._renderExpandCollapseSlot = this.expandable || this._siblingHasNestedItems;
|
|
94
|
+
}
|
|
95
|
+
if (changedProperties.has('_draggingOver') && this._draggingOver && this.dropNested && !this.expanded && this.expandable) {
|
|
96
|
+
let elapsedHoverTime = 0;
|
|
97
|
+
let dragIntervalId = null;
|
|
98
|
+
const watchDraggingOver = () => {
|
|
99
|
+
if (elapsedHoverTime >= dragHoverDropTime) {
|
|
100
|
+
if (this._draggingOver) {
|
|
101
|
+
this._toggleExpandCollapse();
|
|
102
|
+
}
|
|
103
|
+
clearInterval(dragIntervalId);
|
|
104
|
+
} else {
|
|
105
|
+
if (!this._draggingOver) {
|
|
106
|
+
clearInterval(dragIntervalId);
|
|
107
|
+
} else {
|
|
108
|
+
elapsedHoverTime += dragIntervalDelay;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
};
|
|
112
|
+
// check if they are still hovered over same item every 100ms
|
|
113
|
+
dragIntervalId = setInterval(watchDraggingOver, dragIntervalDelay);
|
|
114
|
+
}
|
|
115
|
+
if (changedProperties.has('expanded') || changedProperties.has('_hasNestedList')) {
|
|
116
|
+
this._showNestedLoadingSpinner = this.expanded && !this._hasNestedList;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
updateSiblingHasChildren(siblingHasNestedItems) {
|
|
121
|
+
this._siblingHasNestedItems = siblingHasNestedItems;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
_renderExpandCollapse() {
|
|
125
|
+
if (!this.expandable) {
|
|
126
|
+
return nothing;
|
|
127
|
+
}
|
|
128
|
+
return html`
|
|
129
|
+
<d2l-button-icon
|
|
130
|
+
icon="${this.expanded ? 'tier1:arrow-collapse-small' : 'tier1:arrow-expand-small' }"
|
|
131
|
+
aria-expanded="${this.expanded ? 'true' : 'false'}"
|
|
132
|
+
text="${this.label}"
|
|
133
|
+
@click="${this._toggleExpandCollapse}"></d2l-button-icon>`;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
_renderExpandCollapseAction() {
|
|
137
|
+
if (this.selectable || !this.expandable || this.noPrimaryAction) {
|
|
138
|
+
return nothing;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
return html`<div class="d2l-list-expand-collapse-action" @click="${this._toggleExpandCollapse}"></div>`;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
_renderNestedLoadingSpinner() {
|
|
145
|
+
if (!this.expandable || !this._showNestedLoadingSpinner) {
|
|
146
|
+
return nothing;
|
|
147
|
+
}
|
|
148
|
+
return html`
|
|
149
|
+
<div class="d2l-list-nested-loading">
|
|
150
|
+
<d2l-loading-spinner size="40"></d2l-loading-spinner>
|
|
151
|
+
</div>`;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
_toggleExpandCollapse(e = null) {
|
|
155
|
+
if (e) {
|
|
156
|
+
e.stopPropagation();
|
|
157
|
+
}
|
|
158
|
+
if (!this.expandable) {
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
this.expanded = !this.expanded;
|
|
162
|
+
/** Dispatched whenever the list item expand state is toggled. */
|
|
163
|
+
this.dispatchEvent(new CustomEvent('d2l-list-item-expand-collapse-toggled', {
|
|
164
|
+
composed: true,
|
|
165
|
+
bubbles: true
|
|
166
|
+
}));
|
|
167
|
+
}
|
|
168
|
+
};
|
|
@@ -62,7 +62,8 @@ class ListItemGenericLayout extends RtlMixin(LitElement) {
|
|
|
62
62
|
display: grid;
|
|
63
63
|
grid-template-columns:
|
|
64
64
|
[start outside-control-start] minmax(0, min-content)
|
|
65
|
-
[
|
|
65
|
+
[expand-collapse-start outside-control-end] minmax(0, min-content)
|
|
66
|
+
[control-start expand-collapse-end] minmax(0, min-content)
|
|
66
67
|
[control-end content-start] minmax(0, auto)
|
|
67
68
|
[content-end actions-start] minmax(0, min-content)
|
|
68
69
|
[end actions-end];
|
|
@@ -79,7 +80,7 @@ class ListItemGenericLayout extends RtlMixin(LitElement) {
|
|
|
79
80
|
grid-column: control-start / end;
|
|
80
81
|
}
|
|
81
82
|
:host(.d2l-dragging-over) ::slotted([slot="nested"]) {
|
|
82
|
-
z-index:
|
|
83
|
+
z-index: 7; /* must be greater than item's drop-target to allow dropping onto items within nested list */
|
|
83
84
|
}
|
|
84
85
|
|
|
85
86
|
::slotted([slot="drop-target"]) {
|
|
@@ -87,10 +88,11 @@ class ListItemGenericLayout extends RtlMixin(LitElement) {
|
|
|
87
88
|
position: absolute;
|
|
88
89
|
top: 0;
|
|
89
90
|
width: 100%;
|
|
90
|
-
z-index:
|
|
91
|
+
z-index: 6;
|
|
91
92
|
}
|
|
92
93
|
|
|
93
94
|
::slotted([slot="outside-control"]),
|
|
95
|
+
::slotted([slot="expand-collapse"]),
|
|
94
96
|
::slotted([slot="control"]),
|
|
95
97
|
::slotted([slot="content"]),
|
|
96
98
|
::slotted([slot="actions"]) {
|
|
@@ -98,7 +100,13 @@ class ListItemGenericLayout extends RtlMixin(LitElement) {
|
|
|
98
100
|
}
|
|
99
101
|
::slotted([slot="outside-control"]) {
|
|
100
102
|
grid-column: outside-control-start / outside-control-end;
|
|
101
|
-
width:
|
|
103
|
+
width: fit-content;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
::slotted([slot="expand-collapse"]) {
|
|
107
|
+
cursor: pointer;
|
|
108
|
+
grid-column: expand-collapse-start / expand-collapse-end;
|
|
109
|
+
z-index: 2;
|
|
102
110
|
}
|
|
103
111
|
|
|
104
112
|
::slotted([slot="control"]) {
|
|
@@ -117,7 +125,7 @@ class ListItemGenericLayout extends RtlMixin(LitElement) {
|
|
|
117
125
|
::slotted([slot="actions"]) {
|
|
118
126
|
grid-column: actions-start / actions-end;
|
|
119
127
|
justify-self: end;
|
|
120
|
-
z-index:
|
|
128
|
+
z-index: 5;
|
|
121
129
|
}
|
|
122
130
|
|
|
123
131
|
::slotted([slot="outside-control-action"]),
|
|
@@ -136,14 +144,14 @@ class ListItemGenericLayout extends RtlMixin(LitElement) {
|
|
|
136
144
|
grid-column: control-start / end;
|
|
137
145
|
height: 100%;
|
|
138
146
|
width: 100%;
|
|
139
|
-
z-index:
|
|
147
|
+
z-index: 3;
|
|
140
148
|
}
|
|
141
149
|
:host([no-primary-action]) ::slotted([slot="control-action"]) {
|
|
142
150
|
grid-column: control-start / control-end;
|
|
143
151
|
}
|
|
144
152
|
::slotted([slot="content-action"]) {
|
|
145
153
|
grid-column: content-start / end;
|
|
146
|
-
z-index:
|
|
154
|
+
z-index: 4;
|
|
147
155
|
}
|
|
148
156
|
:host([no-primary-action]) ::slotted([slot="content-action"]) {
|
|
149
157
|
display: none;
|
|
@@ -154,7 +162,7 @@ class ListItemGenericLayout extends RtlMixin(LitElement) {
|
|
|
154
162
|
grid-row: 1 / 2;
|
|
155
163
|
}
|
|
156
164
|
::slotted([slot="control-container"]) {
|
|
157
|
-
grid-column:
|
|
165
|
+
grid-column: expand-collapse-start / end;
|
|
158
166
|
grid-row: 1 / 2;
|
|
159
167
|
}
|
|
160
168
|
`;
|
|
@@ -192,13 +200,14 @@ class ListItemGenericLayout extends RtlMixin(LitElement) {
|
|
|
192
200
|
<slot name="control-container"></slot>
|
|
193
201
|
<slot name="outside-control-container"></slot>
|
|
194
202
|
<slot name="drop-target"></slot>
|
|
195
|
-
<slot name="content-action" class="d2l-cell" data-cell-num="
|
|
203
|
+
<slot name="content-action" class="d2l-cell" data-cell-num="6"></slot>
|
|
196
204
|
<slot name="outside-control-action" class="d2l-cell" data-cell-num="1"></slot>
|
|
197
205
|
<slot name="outside-control" class="d2l-cell" data-cell-num="2"></slot>
|
|
198
206
|
<slot name="control-action" class="d2l-cell" data-cell-num="3"></slot>
|
|
199
|
-
<slot name="
|
|
200
|
-
<slot name="
|
|
201
|
-
<slot name="
|
|
207
|
+
<slot name="expand-collapse" class="d2l-cell" data-cell-num="4"></slot>
|
|
208
|
+
<slot name="control" class="d2l-cell" data-cell-num="5"></slot>
|
|
209
|
+
<slot name="actions" class="d2l-cell" data-cell-num="7"></slot>
|
|
210
|
+
<slot name="content" class="d2l-cell" data-cell-num="8" @focus="${!this.noPrimaryAction ? this._preventFocus : null}"></slot>
|
|
202
211
|
<slot name="nested"></slot>
|
|
203
212
|
`;
|
|
204
213
|
}
|
|
@@ -2,14 +2,18 @@ import '../colors/colors.js';
|
|
|
2
2
|
import './list-item-generic-layout.js';
|
|
3
3
|
import './list-item-placement-marker.js';
|
|
4
4
|
import '../tooltip/tooltip.js';
|
|
5
|
+
import '../expand-collapse/expand-collapse-content.js';
|
|
5
6
|
import { css, html, nothing } from 'lit';
|
|
6
7
|
import { findComposedAncestor, getComposedParent } from '../../helpers/dom.js';
|
|
7
8
|
import { classMap } from 'lit/directives/class-map.js';
|
|
9
|
+
import { composeMixins } from '../../helpers/composeMixins.js';
|
|
8
10
|
import { getFirstFocusableDescendant } from '../../helpers/focus.js';
|
|
9
11
|
import { getUniqueId } from '../../helpers/uniqueId.js';
|
|
10
12
|
import { ifDefined } from 'lit/directives/if-defined.js';
|
|
13
|
+
import { LabelledMixin } from '../../mixins/labelled-mixin.js';
|
|
11
14
|
import { ListItemCheckboxMixin } from './list-item-checkbox-mixin.js';
|
|
12
15
|
import { ListItemDragDropMixin } from './list-item-drag-drop-mixin.js';
|
|
16
|
+
import { ListItemExpandCollapseMixin } from './list-item-expand-collapse-mixin.js';
|
|
13
17
|
import { ListItemRoleMixin } from './list-item-role-mixin.js';
|
|
14
18
|
import { LocalizeCoreElement } from '../../helpers/localize-core-element.js';
|
|
15
19
|
import ResizeObserver from 'resize-observer-polyfill';
|
|
@@ -43,7 +47,18 @@ const ro = new ResizeObserver(entries => {
|
|
|
43
47
|
|
|
44
48
|
const defaultBreakpoints = [842, 636, 580, 0];
|
|
45
49
|
|
|
46
|
-
|
|
50
|
+
/**
|
|
51
|
+
* @property label - The hidden label for the checkbox and expand collapse control
|
|
52
|
+
*/
|
|
53
|
+
export const ListItemMixin = superclass => class extends composeMixins(
|
|
54
|
+
superclass,
|
|
55
|
+
LabelledMixin,
|
|
56
|
+
LocalizeCoreElement,
|
|
57
|
+
ListItemExpandCollapseMixin,
|
|
58
|
+
ListItemDragDropMixin,
|
|
59
|
+
ListItemCheckboxMixin,
|
|
60
|
+
ListItemRoleMixin,
|
|
61
|
+
RtlMixin) {
|
|
47
62
|
|
|
48
63
|
static get properties() {
|
|
49
64
|
return {
|
|
@@ -77,7 +92,8 @@ export const ListItemMixin = superclass => class extends LocalizeCoreElement(Lis
|
|
|
77
92
|
_focusingPrimaryAction: { type: Boolean, attribute: '_focusing-primary-action', reflect: true },
|
|
78
93
|
_highlight: { type: Boolean, reflect: true },
|
|
79
94
|
_highlighting: { type: Boolean, reflect: true },
|
|
80
|
-
_tooltipShowing: { type: Boolean, attribute: '_tooltip-showing', reflect: true }
|
|
95
|
+
_tooltipShowing: { type: Boolean, attribute: '_tooltip-showing', reflect: true },
|
|
96
|
+
_hasNestedList: { state: true }
|
|
81
97
|
};
|
|
82
98
|
}
|
|
83
99
|
|
|
@@ -262,14 +278,16 @@ export const ListItemMixin = superclass => class extends LocalizeCoreElement(Lis
|
|
|
262
278
|
margin-left: 1rem;
|
|
263
279
|
margin-right: 0;
|
|
264
280
|
}
|
|
281
|
+
|
|
265
282
|
d2l-selection-input {
|
|
266
|
-
margin: 0.55rem 0.
|
|
283
|
+
margin: 0.55rem 0.55rem 0.55rem 0;
|
|
267
284
|
}
|
|
268
285
|
.d2l-list-item-content-extend-separators d2l-selection-input {
|
|
269
286
|
margin-left: 0.9rem;
|
|
270
287
|
}
|
|
288
|
+
|
|
271
289
|
d2l-list-item-drag-handle {
|
|
272
|
-
margin: 0.25rem 0
|
|
290
|
+
margin: 0.25rem 0.3rem;
|
|
273
291
|
}
|
|
274
292
|
:host([dir="rtl"]) d2l-selection-input {
|
|
275
293
|
margin-left: 0.9rem;
|
|
@@ -356,6 +374,7 @@ export const ListItemMixin = superclass => class extends LocalizeCoreElement(Lis
|
|
|
356
374
|
this._displayKeyboardTooltip = false;
|
|
357
375
|
this._fullscreenWithin = false;
|
|
358
376
|
this._fullscreenWithinCount = 0;
|
|
377
|
+
this._hasNestedList = false;
|
|
359
378
|
}
|
|
360
379
|
|
|
361
380
|
get breakpoints() {
|
|
@@ -375,6 +394,9 @@ export const ListItemMixin = superclass => class extends LocalizeCoreElement(Lis
|
|
|
375
394
|
if (this.role === 'rowgroup') {
|
|
376
395
|
addTabListener();
|
|
377
396
|
}
|
|
397
|
+
if (!this.selectable && !this.expandable) {
|
|
398
|
+
this.labelRequired = false;
|
|
399
|
+
}
|
|
378
400
|
}
|
|
379
401
|
|
|
380
402
|
disconnectedCallback() {
|
|
@@ -439,6 +461,30 @@ export const ListItemMixin = superclass => class extends LocalizeCoreElement(Lis
|
|
|
439
461
|
else this.scrollIntoView({ behavior: 'smooth', block: alignToTop ? 'start' : 'end' });
|
|
440
462
|
}
|
|
441
463
|
|
|
464
|
+
_getFlattenedListItems(listItem) {
|
|
465
|
+
const listItems = new Map();
|
|
466
|
+
const lazyLoadListItems = new Map();
|
|
467
|
+
this._getListItems(listItems, lazyLoadListItems, listItem);
|
|
468
|
+
return { listItems, lazyLoadListItems };
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
_getListItems(listItems, lazyLoadListItems, listItem) {
|
|
472
|
+
if (!listItem) {
|
|
473
|
+
const rootList = this._getRootList();
|
|
474
|
+
const rootListItems = rootList.getItems();
|
|
475
|
+
rootListItems.forEach(listItem => this._getListItems(listItems, lazyLoadListItems, listItem));
|
|
476
|
+
} else {
|
|
477
|
+
listItems.set(listItem.key, listItem);
|
|
478
|
+
if (listItem.expandable && !listItem._hasNestedList) {
|
|
479
|
+
lazyLoadListItems.set(listItem.key, listItem);
|
|
480
|
+
}
|
|
481
|
+
if (listItem._hasNestedList) {
|
|
482
|
+
const nestedList = listItem._getNestedList();
|
|
483
|
+
nestedList.getItems().forEach(listItem => this._getListItems(listItems, lazyLoadListItems, listItem));
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
|
|
442
488
|
_getNestedList() {
|
|
443
489
|
if (!this.shadowRoot) return;
|
|
444
490
|
const nestedSlot = this.shadowRoot.querySelector('slot[name="nested"]');
|
|
@@ -458,6 +504,16 @@ export const ListItemMixin = superclass => class extends LocalizeCoreElement(Lis
|
|
|
458
504
|
}
|
|
459
505
|
}
|
|
460
506
|
|
|
507
|
+
_getParentList(node) {
|
|
508
|
+
if (!node) node = this;
|
|
509
|
+
let parentList;
|
|
510
|
+
while (parentList?.tagName !== 'D2L-LIST') {
|
|
511
|
+
node = getComposedParent(node);
|
|
512
|
+
if (node.tagName === 'D2L-LIST') parentList = node;
|
|
513
|
+
}
|
|
514
|
+
return parentList;
|
|
515
|
+
}
|
|
516
|
+
|
|
461
517
|
_getParentListItem() {
|
|
462
518
|
const parentListItem = findComposedAncestor(this.parentNode, node => this._isListItem(node));
|
|
463
519
|
return parentListItem;
|
|
@@ -530,6 +586,18 @@ export const ListItemMixin = superclass => class extends LocalizeCoreElement(Lis
|
|
|
530
586
|
this._hovering = false;
|
|
531
587
|
}
|
|
532
588
|
|
|
589
|
+
_onNestedSlotChange() {
|
|
590
|
+
if (this.selectable) {
|
|
591
|
+
this._onNestedSlotChangeCheckboxMixin();
|
|
592
|
+
}
|
|
593
|
+
const nestedList = this._getNestedList();
|
|
594
|
+
if (this._hasNestedList !== !!nestedList) {
|
|
595
|
+
this._hasNestedList = !!nestedList;
|
|
596
|
+
/** @ignore */
|
|
597
|
+
this.dispatchEvent(new CustomEvent('d2l-list-item-nested-change', { bubbles: true, composed: true }));
|
|
598
|
+
}
|
|
599
|
+
}
|
|
600
|
+
|
|
533
601
|
_renderListItem({ illustration, content, actions, nested } = {}) {
|
|
534
602
|
const classes = {
|
|
535
603
|
'd2l-visible-on-ancestor-target': true,
|
|
@@ -539,7 +607,6 @@ export const ListItemMixin = superclass => class extends LocalizeCoreElement(Lis
|
|
|
539
607
|
|
|
540
608
|
const primaryAction = ((!this.noPrimaryAction && this._renderPrimaryAction) ? this._renderPrimaryAction(this._contentId) : null);
|
|
541
609
|
const tooltipForId = (primaryAction ? this._primaryActionId : (this.selectable ? this._checkboxId : null));
|
|
542
|
-
|
|
543
610
|
const innerView = html`
|
|
544
611
|
<d2l-list-item-generic-layout
|
|
545
612
|
align-nested="${ifDefined(this.draggable && this.selectable ? 'control' : undefined)}"
|
|
@@ -556,12 +623,15 @@ export const ListItemMixin = superclass => class extends LocalizeCoreElement(Lis
|
|
|
556
623
|
${this._renderDragHandle(this._renderOutsideControl)}
|
|
557
624
|
${this._renderDragTarget(this.dragTargetHandleOnly ? this._renderOutsideControlHandleOnly : this._renderOutsideControlAction)}
|
|
558
625
|
<div slot="control-container"></div>
|
|
559
|
-
${this.
|
|
560
|
-
|
|
561
|
-
|
|
626
|
+
<div slot="expand-collapse" class="d2l-list-expand-collapse" @click="${this._toggleExpandCollapse}">
|
|
627
|
+
${this._renderExpandCollapse()}
|
|
628
|
+
</div>
|
|
629
|
+
${this.selectable ? html`<div slot="control">${this._renderCheckbox()}</div>` : nothing}
|
|
630
|
+
${this.selectable || this.expandable ? html`<div slot="control-action"
|
|
562
631
|
@mouseenter="${this._onMouseEnter}"
|
|
563
632
|
@mouseleave="${this._onMouseLeave}">
|
|
564
633
|
${this._renderCheckboxAction('')}
|
|
634
|
+
${this._renderExpandCollapseAction()}
|
|
565
635
|
</div>` : nothing }
|
|
566
636
|
${primaryAction ? html`
|
|
567
637
|
<div slot="content-action"
|
|
@@ -583,9 +653,7 @@ export const ListItemMixin = superclass => class extends LocalizeCoreElement(Lis
|
|
|
583
653
|
class="d2l-list-item-actions-container">
|
|
584
654
|
<slot name="actions" class="d2l-list-item-actions">${actions}</slot>
|
|
585
655
|
</div>
|
|
586
|
-
|
|
587
|
-
<slot name="nested" @slotchange="${this._onNestedSlotChange}">${nested}</slot>
|
|
588
|
-
</div>
|
|
656
|
+
${this._renderNested(nested)}
|
|
589
657
|
</d2l-list-item-generic-layout>
|
|
590
658
|
`;
|
|
591
659
|
|
|
@@ -598,6 +666,15 @@ export const ListItemMixin = superclass => class extends LocalizeCoreElement(Lis
|
|
|
598
666
|
|
|
599
667
|
}
|
|
600
668
|
|
|
669
|
+
_renderNested(nested) {
|
|
670
|
+
const nestedSlot = html`<slot name="nested" @slotchange="${this._onNestedSlotChange}">${nested}</slot>`;
|
|
671
|
+
return html`
|
|
672
|
+
<div slot="nested" @d2l-selection-provider-connected="${this._onSelectionProviderConnected}">
|
|
673
|
+
${this.expandable ? html`<d2l-expand-collapse-content ?expanded="${this.expanded}">${this._renderNestedLoadingSpinner()}${nestedSlot}</d2l-expand-collapse-content>` : nestedSlot}
|
|
674
|
+
</div>
|
|
675
|
+
`;
|
|
676
|
+
}
|
|
677
|
+
|
|
601
678
|
_renderOutsideControl(dragHandle) {
|
|
602
679
|
return html`<div slot="outside-control">${dragHandle}</div>`;
|
|
603
680
|
}
|
package/components/list/list.js
CHANGED
|
@@ -2,6 +2,7 @@ import { css, html, LitElement } from 'lit';
|
|
|
2
2
|
import { getNextFocusable, getPreviousFocusable } from '../../helpers/focus.js';
|
|
3
3
|
import { SelectionInfo, SelectionMixin } from '../selection/selection-mixin.js';
|
|
4
4
|
import { PageableMixin } from '../paging/pageable-mixin.js';
|
|
5
|
+
import { SubscriberRegistryController } from '../../controllers/subscriber/subscriberControllers.js';
|
|
5
6
|
|
|
6
7
|
const keyCodes = {
|
|
7
8
|
TAB: 9
|
|
@@ -66,11 +67,19 @@ class List extends PageableMixin(SelectionMixin(LitElement)) {
|
|
|
66
67
|
this._itemsShowingCount = 0;
|
|
67
68
|
this._itemsShowingTotalCount = 0;
|
|
68
69
|
this._listItemChanges = [];
|
|
70
|
+
this._childHasExpandCollapseToggle = false;
|
|
71
|
+
|
|
72
|
+
this._listChildrenUpdatedSubscribers = new SubscriberRegistryController(
|
|
73
|
+
this,
|
|
74
|
+
{ onSubscribe: this._updateActiveSubscriber.bind(this), updateSubscribers: this._updateActiveSubscribers.bind(this) },
|
|
75
|
+
{ eventName: 'd2l-list-child-status' }
|
|
76
|
+
);
|
|
69
77
|
}
|
|
70
78
|
|
|
71
79
|
connectedCallback() {
|
|
72
80
|
super.connectedCallback();
|
|
73
81
|
this.addEventListener('d2l-list-items-showing-count-change', this._handleListItemsShowingCountChange);
|
|
82
|
+
this.addEventListener('d2l-list-item-nested-change', (e) => this._handleListIemNestedChange(e));
|
|
74
83
|
}
|
|
75
84
|
|
|
76
85
|
disconnectedCallback() {
|
|
@@ -80,7 +89,8 @@ class List extends PageableMixin(SelectionMixin(LitElement)) {
|
|
|
80
89
|
|
|
81
90
|
firstUpdated(changedProperties) {
|
|
82
91
|
super.firstUpdated(changedProperties);
|
|
83
|
-
|
|
92
|
+
// check if list items are expandable on first render so we adjust sibling spacing appropriately
|
|
93
|
+
this._handleListIemNestedChange();
|
|
84
94
|
this.addEventListener('d2l-list-item-selected', e => {
|
|
85
95
|
|
|
86
96
|
// batch the changes from select-all and nested lists
|
|
@@ -174,6 +184,10 @@ class List extends PageableMixin(SelectionMixin(LitElement)) {
|
|
|
174
184
|
return new SelectionInfo(keys, selectionInfo.state);
|
|
175
185
|
}
|
|
176
186
|
|
|
187
|
+
getSubscriberController() {
|
|
188
|
+
return this._listChildrenUpdatedSubscribers;
|
|
189
|
+
}
|
|
190
|
+
|
|
177
191
|
_getItemByIndex(index) {
|
|
178
192
|
const items = this.getItems();
|
|
179
193
|
if (index > items.length - 1) return;
|
|
@@ -189,16 +203,14 @@ class List extends PageableMixin(SelectionMixin(LitElement)) {
|
|
|
189
203
|
return this._itemsShowingCount - 1;
|
|
190
204
|
}
|
|
191
205
|
|
|
206
|
+
_getLazyLoadItems() {
|
|
207
|
+
const items = this.getItems();
|
|
208
|
+
return items.length > 0 ? items[0]._getFlattenedListItems().lazyLoadListItems : new Map();
|
|
209
|
+
}
|
|
210
|
+
|
|
192
211
|
async _getListItemsShowingTotalCount(refresh) {
|
|
193
212
|
if (refresh) {
|
|
194
|
-
this._itemsShowingTotalCount =
|
|
195
|
-
await item.updateComplete;
|
|
196
|
-
if (item._selectionProvider) {
|
|
197
|
-
return (await count + await item._selectionProvider._getListItemsShowingTotalCount(true));
|
|
198
|
-
} else {
|
|
199
|
-
return await count;
|
|
200
|
-
}
|
|
201
|
-
}, this._itemsShowingCount);
|
|
213
|
+
this._itemsShowingTotalCount = this.getItems().length;
|
|
202
214
|
}
|
|
203
215
|
return this._itemsShowingTotalCount;
|
|
204
216
|
}
|
|
@@ -212,6 +224,22 @@ class List extends PageableMixin(SelectionMixin(LitElement)) {
|
|
|
212
224
|
if (focusable) focusable.focus();
|
|
213
225
|
}
|
|
214
226
|
|
|
227
|
+
_handleListIemNestedChange(e) {
|
|
228
|
+
if (e) {
|
|
229
|
+
e.stopPropagation();
|
|
230
|
+
}
|
|
231
|
+
const items = this.getItems();
|
|
232
|
+
let aChildHasToggleEnabled = false;
|
|
233
|
+
for (const item of items) {
|
|
234
|
+
if (item.expandable) {
|
|
235
|
+
aChildHasToggleEnabled = true;
|
|
236
|
+
break;
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
this._childHasExpandCollapseToggle = aChildHasToggleEnabled;
|
|
240
|
+
this._listChildrenUpdatedSubscribers.updateSubscribers();
|
|
241
|
+
}
|
|
242
|
+
|
|
215
243
|
_handleListItemsShowingCountChange() {
|
|
216
244
|
if (this.slot === 'nested') return;
|
|
217
245
|
|
|
@@ -242,6 +270,14 @@ class List extends PageableMixin(SelectionMixin(LitElement)) {
|
|
|
242
270
|
}));
|
|
243
271
|
}
|
|
244
272
|
|
|
273
|
+
_updateActiveSubscriber(subscriber) {
|
|
274
|
+
subscriber.updateSiblingHasChildren(this._childHasExpandCollapseToggle);
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
_updateActiveSubscribers(subscribers) {
|
|
278
|
+
subscribers.forEach(subscriber => subscriber.updateSiblingHasChildren(this._childHasExpandCollapseToggle));
|
|
279
|
+
}
|
|
280
|
+
|
|
245
281
|
}
|
|
246
282
|
|
|
247
283
|
customElements.define('d2l-list', List);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { css, html, LitElement } from 'lit';
|
|
1
|
+
import { css, html, LitElement, nothing } from 'lit';
|
|
2
2
|
import { bodyCompactStyles } from '../typography/styles.js';
|
|
3
3
|
import { LocalizeCoreElement } from '../../helpers/localize-core-element.js';
|
|
4
4
|
import { SelectionInfo } from './selection-mixin.js';
|
|
@@ -35,25 +35,56 @@ class Summary extends LocalizeCoreElement(SelectionObserverMixin(LitElement)) {
|
|
|
35
35
|
}
|
|
36
36
|
|
|
37
37
|
render() {
|
|
38
|
+
if (!this._summary) {
|
|
39
|
+
return nothing;
|
|
40
|
+
}
|
|
41
|
+
return html`
|
|
42
|
+
<p class="d2l-body-compact">${this._summary}</p>
|
|
43
|
+
`;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
willUpdate(changedProperties) {
|
|
47
|
+
if (changedProperties.has('_provider') || changedProperties.has('selectionInfo')) {
|
|
48
|
+
this._updateSelectSummary();
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
_updateSelectSummary() {
|
|
38
53
|
if (this._provider && this._provider.selectionSingle) return;
|
|
39
54
|
|
|
40
55
|
let count;
|
|
41
|
-
let summary;
|
|
42
56
|
if (this._provider && this._provider.selectionCountOverride !== undefined) {
|
|
43
57
|
count = this._provider.selectionCountOverride;
|
|
44
|
-
|
|
45
|
-
this.noSelectionText : this.localize('components.selection.selected', 'count', count)
|
|
58
|
+
this._summary = this._provider.selectionCountOverride === 0 && this.noSelectionText ?
|
|
59
|
+
this.noSelectionText : this.localize('components.selection.selected', 'count', count);
|
|
46
60
|
} else {
|
|
47
|
-
|
|
48
|
-
|
|
61
|
+
/* If lazy loading items is supported (ex. d2l-list) then check the keys to determine if the plus sign should be included.
|
|
62
|
+
* If lazy loading is not supported (ex. d2l-table-wrapper), then skip this.
|
|
63
|
+
*/
|
|
64
|
+
let includePlus = false;
|
|
65
|
+
if (this._provider && this._provider._getLazyLoadItems) {
|
|
66
|
+
const lazyLoadListItems = this._provider._getLazyLoadItems();
|
|
67
|
+
if (lazyLoadListItems.size > 0) {
|
|
68
|
+
for (const selectedItemKey of this.selectionInfo.keys) {
|
|
69
|
+
if (lazyLoadListItems.has(selectedItemKey)) {
|
|
70
|
+
includePlus = true;
|
|
71
|
+
break;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
49
76
|
|
|
50
|
-
|
|
51
|
-
this.
|
|
52
|
-
}
|
|
77
|
+
count = this.selectionInfo.state === SelectionInfo.states.allPages ?
|
|
78
|
+
this._provider.itemCount : this.selectionInfo.keys.length;
|
|
53
79
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
80
|
+
if (this.selectionInfo.state === SelectionInfo.states.none && this.noSelectionText) {
|
|
81
|
+
this._summary = this.noSelectionText;
|
|
82
|
+
} else if (includePlus) {
|
|
83
|
+
this._summary = this.localize('components.selection.selected-plus', 'count', count);
|
|
84
|
+
} else {
|
|
85
|
+
this._summary = this.localize('components.selection.selected', 'count', count);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
57
88
|
}
|
|
58
89
|
|
|
59
90
|
}
|