diff --git a/components/list/demo/demo-list-nav.js b/components/list/demo/demo-list-nav.js
index a38d0161317..535064d280d 100644
--- a/components/list/demo/demo-list-nav.js
+++ b/components/list/demo/demo-list-nav.js
@@ -45,9 +45,9 @@ class ListDemoNav extends LitElement {
render() {
return html`
-
${repeat(this.#list, (item) => item.key, (item) => this._renderItem(item))}
@@ -149,8 +149,9 @@ class ListDemoNav extends LitElement {
drop-nested
label="${item.primaryText}"
prevent-navigation>
+ ${item.hasIcon ? html`` : nothing}
- ${item.hasIcon ? html`` : nothing}${item.primaryText}
+ ${item.primaryText}
${item.tooltipOpenerText && item.tooltipText
? html`${item.tooltipText}
`
: nothing
diff --git a/components/list/demo/list-demo-scenarios.js b/components/list/demo/list-demo-scenarios.js
index 099f58adb77..326086de835 100644
--- a/components/list/demo/list-demo-scenarios.js
+++ b/components/list/demo/list-demo-scenarios.js
@@ -372,9 +372,9 @@ export const listDemos = {
primaryText: 'Wetland Engineering #2',
dropNested: true,
items: [],
- hasIcon: true
+ hasIcon: false
}],
- hasIcon: true
+ hasIcon: false
}]
}],
};
diff --git a/components/list/list-item-expand-collapse-mixin.js b/components/list/list-item-expand-collapse-mixin.js
index d4026c85c51..8543d57a0d2 100644
--- a/components/list/list-item-expand-collapse-mixin.js
+++ b/components/list/list-item-expand-collapse-mixin.js
@@ -91,7 +91,7 @@ export const ListItemExpandCollapseMixin = superclass => class extends SkeletonM
updated(changedProperties) {
if (changedProperties.has('_siblingHasNestedItems') || changedProperties.has('expandable')) {
- this._renderExpandCollapseSlot = this.expandable || this._siblingHasNestedItems;
+ this._renderExpandCollapseSlot = this.expandable;
}
if (changedProperties.has('_draggingOver') && this._draggingOver && this.dropNested && !this.expanded && this.expandable) {
let elapsedHoverTime = 0;
diff --git a/components/list/list-item-generic-layout.js b/components/list/list-item-generic-layout.js
index af9ed5d013a..1307a6b78cb 100644
--- a/components/list/list-item-generic-layout.js
+++ b/components/list/list-item-generic-layout.js
@@ -82,7 +82,8 @@ class ListItemGenericLayout extends LitElement {
[color-start outside-control-end] minmax(0, min-content)
[expand-collapse-start color-end] minmax(0, min-content)
[control-start expand-collapse-end] minmax(0, min-content)
- [control-end content-start] minmax(0, auto)
+ [spacer-start control-end] minmax(0, min-content)
+ [spacer-end content-start] minmax(0, auto)
[content-end actions-start] minmax(0, min-content)
[end actions-end];
grid-template-rows:
@@ -144,8 +145,9 @@ class ListItemGenericLayout extends LitElement {
grid-column: color-start / color-end;
}
- ::slotted([slot="before-content"]) {
- grid-column: color-start / content-start;
+ ::slotted([slot="spacer"]) {
+ grid-column: spacer-start / spacer-end;
+ grid-row: 2 / 3;
}
::slotted([slot="control-action"]) ~ ::slotted([slot="content"]),
@@ -300,6 +302,7 @@ class ListItemGenericLayout extends LitElement {
+
diff --git a/components/list/list-item-mixin.js b/components/list/list-item-mixin.js
index 1e33864873c..18be4d4e54c 100644
--- a/components/list/list-item-mixin.js
+++ b/components/list/list-item-mixin.js
@@ -266,6 +266,10 @@ export const ListItemMixin = superclass => class extends composeMixins(
padding-inline-start: 2.2rem; /* width of "control" slot set in generic-layout */
}
+ .d2l-list-item-spacer {
+ width: var(--d2l-list-item-spacer-width, 0);
+ }
+
[slot="content"] ::slotted([slot="illustration"]),
[slot="content"] .d2l-list-item-illustration > * {
border-radius: 6px;
@@ -871,6 +875,7 @@ export const ListItemMixin = superclass => class extends composeMixins(
${this._renderExpandCollapse()}
${this.selectable ? html`${this._renderCheckbox()}
` : nothing}
+
${this.selectable || this.expandable ? html`
class extends ListItemLinkMixin(su
.d2l-list-item-content ::slotted(*) {
width: 100%;
}
+ .d2l-list-item-content ::slotted([slot="illustration"]) {
+ width: auto;
+ }
:host([current]) [slot="outside-control-container"] {
background-color: var(--d2l-color-regolith);
border: 3px solid var(--d2l-color-celestine);
diff --git a/components/list/list.js b/components/list/list.js
index 18c3ffb88eb..b8c4fdd5bfe 100644
--- a/components/list/list.js
+++ b/components/list/list.js
@@ -164,6 +164,7 @@ class List extends PageableMixin(SelectionMixin(LitElement)) {
this._breakpoint = 0;
this._slimColor = false;
this._width = 0;
+ this._spacerMeasurementScheduled = false;
this._listChildrenUpdatedSubscribers = new SubscriberRegistryController(this, 'list-child-status', {
onSubscribe: this._updateActiveSubscriber.bind(this),
@@ -258,7 +259,7 @@ class List extends PageableMixin(SelectionMixin(LitElement)) {
}
getItems(slot) {
- if (!this.shadowRoot) return;
+ if (!this.shadowRoot) return [];
if (!slot) slot = this.shadowRoot.querySelector('slot:not([name])');
if (!slot) return [];
return slot.assignedNodes({ flatten: true }).filter((node) => {
@@ -334,6 +335,7 @@ class List extends PageableMixin(SelectionMixin(LitElement)) {
return true;
}
});
+ this._updateSpacerSlotWidth();
}
setSelectionForAll(selected, selectAllPages) {
@@ -360,6 +362,29 @@ class List extends PageableMixin(SelectionMixin(LitElement)) {
return items.length > 0 ? items[0]._getFlattenedListItems().lazyLoadListItems : new Map();
}
+ _getSpacerWidth(item) {
+ const shadowRoot = item.shadowRoot;
+ if (!shadowRoot) return 0;
+
+ const layout = shadowRoot.querySelector('d2l-list-item-generic-layout');
+ const content = shadowRoot.querySelector('[slot="content"]');
+ const spacer = shadowRoot.querySelector('[slot="spacer"]');
+ if (!layout || !content || layout.offsetParent === null || content.offsetParent === null) return 0;
+
+ const layoutRect = layout.getBoundingClientRect();
+ const contentRect = content.getBoundingClientRect();
+ const spacerWidth = spacer ? spacer.getBoundingClientRect().width : 0;
+ const isRtl = getComputedStyle(layout).direction === 'rtl';
+ const inlineStartWidth = isRtl ? layoutRect.right - contentRect.right : contentRect.left - layoutRect.left;
+ let totalWidth = Math.max(0, inlineStartWidth - spacerWidth);
+
+ const illustrationSlot = content.querySelector('.d2l-list-item-illustration');
+ const illustration = illustrationSlot?.assignedElements({ flatten: true })?.[0];
+ totalWidth += this._measureWithMargins(illustration);
+
+ return totalWidth;
+ }
+
_handleKeyDown(e) {
if (!this.grid || this.slot === 'nested' || e.keyCode !== keyCodes.TAB) return;
e.preventDefault();
@@ -394,6 +419,7 @@ class List extends PageableMixin(SelectionMixin(LitElement)) {
this._childHasColor = aChildHasColor;
this._childHasExpandCollapseToggle = aChildHasToggleEnabled;
this._listChildrenUpdatedSubscribers.updateSubscribers();
+ this._updateSpacerSlotWidth();
}
_handleListItemPropertyChange(e) {
@@ -491,6 +517,7 @@ class List extends PageableMixin(SelectionMixin(LitElement)) {
});
this._updateItemLayouts(items);
+ this._updateSpacerSlotWidth();
/** @ignore */
this.dispatchEvent(new CustomEvent('d2l-list-item-showing-count-change', {
@@ -499,6 +526,14 @@ class List extends PageableMixin(SelectionMixin(LitElement)) {
}));
}
+ _measureWithMargins(element) {
+ if (!element || element.offsetParent === null) return 0;
+ const rect = element.getBoundingClientRect();
+ const styles = getComputedStyle(element);
+ const margin = (parseFloat(styles.marginInlineStart) || 0) + (parseFloat(styles.marginInlineEnd) || 0);
+ return rect.width + margin;
+ }
+
_updateActiveSubscriber(subscriber) {
subscriber.updateSiblingHasChildren(this._childHasExpandCollapseToggle);
subscriber.updateSiblingHasColor(this._childHasColor);
@@ -518,6 +553,35 @@ class List extends PageableMixin(SelectionMixin(LitElement)) {
items.forEach(item => item.layout = (this.layout === listLayouts.tiles ? 'tile' : 'normal'));
}
+ _updateSpacerSlotWidth() {
+ if (this._spacerMeasurementScheduled) return;
+ this._spacerMeasurementScheduled = true;
+
+ requestAnimationFrame(() => {
+ this._spacerMeasurementScheduled = false;
+
+ const items = this.getItems() || [];
+ if (items.length === 0) return;
+
+ let maxWidth = 0;
+ const measurements = [];
+
+ for (const item of items) {
+ const totalWidth = this._getSpacerWidth(item);
+ if (totalWidth === 0) continue;
+ measurements.push({ item, width: totalWidth });
+ if (totalWidth > maxWidth) maxWidth = totalWidth;
+ }
+
+ if (measurements.length === 0) return;
+
+ for (const { item, width } of measurements) {
+ if (!item?.style) continue;
+ item.style.setProperty('--d2l-list-item-spacer-width', `${Math.max(0, maxWidth - width)}px`);
+ }
+ });
+ }
+
}
customElements.define('d2l-list', List);