Skip to content

Commit 6d47d68

Browse files
authored
feat(ui5-avatar-group): new slot overflowButton (#3037)
Added new slot which allows to add custom overflow button and "overflow" event to let you know when avatars hide/show in order to update the overflow button text. If not provided, the AvatarGroup will display the built-in overflow button. The slot is provided to allow you defining the text of the overflow button and let you set additional aria attributes, such as aria-label. Fixes: #2912
1 parent 249334a commit 6d47d68

File tree

4 files changed

+87
-13
lines changed

4 files changed

+87
-13
lines changed

packages/main/src/AvatarGroup.hbs

+7-3
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,12 @@
1010
>
1111
<slot></slot>
1212

13-
<ui5-button ?hidden="{{_overflowBtnHidden}}" tabindex="{{_overflowButtonTabIndex}}" class="ui5-avatar-group-overflow-btn">
14-
{{_overflowButtonText}}
15-
</ui5-button>
13+
{{#if _customOverflowButton}}
14+
<slot name="overflowButton"></slot>
15+
{{else}}
16+
<ui5-button ?hidden="{{_overflowBtnHidden}}" ?non-interactive={{_isGroup}} class="ui5-avatar-group-overflow-btn">
17+
{{_overflowButtonText}}
18+
</ui5-button>
19+
{{/if}}
1620
</div>
1721
</div>

packages/main/src/AvatarGroup.js

+42-9
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,20 @@ const metadata = {
113113
type: HTMLElement,
114114
propertyName: "items",
115115
},
116+
/**
117+
* Defines the overflow button of <code>ui5-avatar-group</code>.
118+
* <b>Note:</b> We recommend using the <code>ui5-button</code> component.
119+
* <br><br>
120+
* <b>Note:</b> If this slot is not used, the <code>ui5-avatar-group</code> will
121+
* display the built-in overflow button.
122+
* @type {HTMLElement}
123+
* @slot overflowButton
124+
* @public
125+
* @since 1.0.0-rc.13
126+
*/
127+
overflowButton: {
128+
type: HTMLElement,
129+
},
116130
},
117131
events: /** @lends sap.ui.webcomponents.main.AvatarGroup.prototype */ {
118132
/**
@@ -130,6 +144,14 @@ const metadata = {
130144
overflowButtonClicked: { type: Boolean },
131145
},
132146
},
147+
/**
148+
* Fired when the count of visible <code>ui5-avatar</code> elements in the
149+
* <code>ui5-avatar-group</code> has changed
150+
* @event
151+
* @public
152+
* @since 1.0.0-rc.13
153+
*/
154+
overflow: {},
133155
},
134156
};
135157

@@ -241,6 +263,10 @@ class AvatarGroup extends UI5Element {
241263
return this.items.map(avatar => avatar._effectiveBackgroundColor);
242264
}
243265

266+
get _customOverflowButton() {
267+
return this.overflowButton.length ? this.overflowButton[0] : undefined;
268+
}
269+
244270
get _hiddenStartIndex() {
245271
return this._itemsCount - this._hiddenItems;
246272
}
@@ -261,10 +287,6 @@ class AvatarGroup extends UI5Element {
261287
return this._isGroup ? "0" : "-1";
262288
}
263289

264-
get _overflowButtonTabIndex() {
265-
return this._isGroup ? "-1" : false;
266-
}
267-
268290
get _overflowButton() {
269291
return this.shadowRoot.querySelector(AVATAR_GROUP_OVERFLOW_BTN_SELECTOR);
270292
}
@@ -278,26 +300,31 @@ class AvatarGroup extends UI5Element {
278300
* @private
279301
*/
280302
get _overflowButtonEffectiveWidth() {
303+
const button = this._customOverflowButton ? this._customOverflowButton : this._overflowButton;
281304
// if in "Group" mode overflow button size is equal to the offset from second item
282305
if (this._isGroup) {
283306
let item = this.items[1];
284307

285308
// in some cases when second avatar is overflowed the offset of the button is the right one
286309
if (!item || item.hidden) {
287-
item = this._overflowButton;
310+
item = button;
288311
}
289312

290313
return this.effectiveDir === "rtl" ? this._getWidthToItem(item) : item.offsetLeft;
291314
}
292315

293-
return this._overflowButton.offsetWidth;
316+
return button.offsetWidth;
294317
}
295318

296319
onAfterRendering() {
297320
this._overflowItems();
298321
}
299322

300323
onBeforeRendering() {
324+
if (this._customOverflowButton) {
325+
this._customOverflowButton.nonInteractive = this._isGroup;
326+
}
327+
301328
this._prepareAvatars();
302329
}
303330

@@ -332,7 +359,7 @@ class AvatarGroup extends UI5Element {
332359
}
333360

334361
_fireGroupEvent(targetRef) {
335-
const isOverflowButtonClicked = targetRef.classList.contains(OVERFLOW_BTN_CLASS);
362+
const isOverflowButtonClicked = targetRef.classList.contains(OVERFLOW_BTN_CLASS) || targetRef === this._customOverflowButton;
336363

337364
this.fireEvent("click", {
338365
targetRef,
@@ -384,7 +411,7 @@ class AvatarGroup extends UI5Element {
384411
}
385412

386413
// last avatar should not be offset as it breaks the container width and focus styles are no set correctly
387-
if (index !== this._itemsCount - 1) {
414+
if (index !== this._itemsCount - 1 || this._customOverflowButton) {
388415
// based on RTL margin left or right is set to avatars
389416
avatar.style[`margin-${RTL ? "left" : "right"}`] = offsets[avatar._effectiveSize][this.type];
390417
}
@@ -437,7 +464,7 @@ class AvatarGroup extends UI5Element {
437464
// used to determine whether the following items will fit the container or not
438465
let totalWidth = this._getWidthToItem(item) + item.offsetWidth;
439466

440-
if (index !== this._itemsCount - 1) {
467+
if (index !== this._itemsCount - 1 || this._customOverflowButton) {
441468
totalWidth += this._overflowButtonEffectiveWidth;
442469
}
443470

@@ -460,13 +487,19 @@ class AvatarGroup extends UI5Element {
460487
}
461488

462489
_setHiddenItems(hiddenItems) {
490+
const shouldFireEvent = this._hiddenItems !== hiddenItems;
491+
463492
this._hiddenItems = hiddenItems;
464493

465494
this.items.forEach((item, index) => {
466495
item.hidden = index >= this._hiddenStartIndex;
467496
});
468497

469498
this._overflowButtonText = `+${hiddenItems > 99 ? 99 : hiddenItems}`;
499+
500+
if (shouldFireEvent) {
501+
this.fireEvent("overflow");
502+
}
470503
}
471504
}
472505

packages/main/src/themes/AvatarGroup.css

+10-1
Original file line numberDiff line numberDiff line change
@@ -26,49 +26,58 @@
2626
cursor: pointer;
2727
}
2828

29+
:host([type="Group"]) ::slotted([ui5-button]),
2930
:host([type="Group"]) ::slotted([ui5-avatar]) {
3031
pointer-events: none;
3132
}
3233

34+
::slotted([ui5-button]:not([hidden])),
3335
.ui5-avatar-group-overflow-btn:not([hidden]) {
3436
border-radius: 50%;
3537
display: inline-flex;
36-
text-overflow: unset;
38+
text-overflow: initial;
3739
}
3840

41+
::slotted([ui5-button][focused]),
3942
.ui5-avatar-group-overflow-btn[focused] {
4043
outline: var(--_ui5_button_outline);
4144
outline-offset: var(--_ui5_avatar_group_overflow_button_focus_offset);
4245
}
4346

47+
:host([avatar-size="XS"]) ::slotted([ui5-button]),
4448
:host([avatar-size="XS"]) .ui5-avatar-group-overflow-btn {
4549
height: 2rem;
4650
width: 2rem;
4751
min-width: 2rem;
4852
font-size: .75rem;
4953
}
5054

55+
::slotted([ui5-button]),
56+
:host([avatar-size="S"]) ::slotted([ui5-button]),
5157
:host([avatar-size="S"]) .ui5-avatar-group-overflow-btn {
5258
height: 3rem;
5359
width: 3rem;
5460
min-width: 3rem;
5561
font-size: 1.125rem;
5662
}
5763

64+
:host([avatar-size="M"]) ::slotted([ui5-button]),
5865
:host([avatar-size="M"]) .ui5-avatar-group-overflow-btn {
5966
height: 4rem;
6067
width: 4rem;
6168
min-width: 4rem;
6269
font-size: 1.625rem;
6370
}
6471

72+
:host([avatar-size="L"]) ::slotted([ui5-button]),
6573
:host([avatar-size="L"]) .ui5-avatar-group-overflow-btn {
6674
height: 5rem;
6775
width: 5rem;
6876
min-width: 5rem;
6977
font-size: 2rem;
7078
}
7179

80+
:host([avatar-size="XL"]) ::slotted([ui5-button]),
7281
:host([avatar-size="XL"]) .ui5-avatar-group-overflow-btn {
7382
height: 7rem;
7483
width: 7rem;

packages/main/test/pages/AvatarGroup.html

+28
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,21 @@ <h3>Unordinary group</h3>
213213
<ui5-avatar size="L" interactive initials="L"></ui5-avatar>
214214
<ui5-avatar size="XL" interactive initials="XL"></ui5-avatar>
215215
</ui5-avatar-group>
216+
217+
<h3>AvatarGroup with user defined overflow button</h3>
218+
<ui5-avatar-group type="Individual" id="group-with-overflow-btn">
219+
<ui5-avatar size="XS" interactive initials="XS"></ui5-avatar>
220+
<ui5-avatar size="S" interactive initials="S"></ui5-avatar>
221+
<ui5-avatar size="M" interactive initials="M"></ui5-avatar>
222+
<ui5-avatar size="L" interactive initials="L"></ui5-avatar>
223+
<ui5-avatar size="XL" interactive initials="XL"></ui5-avatar>
224+
<ui5-avatar size="XS" interactive initials="XS"></ui5-avatar>
225+
<ui5-avatar size="S" interactive initials="S"></ui5-avatar>
226+
<ui5-avatar size="M" interactive initials="M"></ui5-avatar>
227+
<ui5-avatar size="L" interactive initials="L"></ui5-avatar>
228+
<ui5-avatar size="XL" interactive initials="XL"></ui5-avatar>
229+
<ui5-button slot="overflowButton" id="overflowButton" aria-label="+99 avatars">+99</ui5-button>
230+
</ui5-avatar-group>
216231
</div>
217232

218233
<script>
@@ -230,6 +245,8 @@ <h3>Unordinary group</h3>
230245
var resetBtn = document.getElementById("reset-btn")
231246
var slider = document.getElementById("slider");
232247
var avGrWrapper = document.getElementById("avatar-group-wrapper");
248+
var groupWithBtn = document.getElementById("group-with-overflow-btn");
249+
var customOBtn = document.getElementById("overflowButton");
233250

234251
slider.style.width = '100%';
235252
avGrWrapper.style.width = slider.getAttribute("value") + '%';
@@ -300,6 +317,17 @@ <h3>Unordinary group</h3>
300317
eventTargetRef.value = "";
301318
eventOverflowButtonClicked.value = "";
302319
});
320+
321+
function formatBtnText(group) {
322+
var totalAvatars = 90 - group.items.length + group.hiddenItems.length;
323+
324+
customOBtn.innerHTML = totalAvatars > 99 ? "+99" : "+" + totalAvatars;
325+
customOBtn.ariaLabel = totalAvatars > 99 ? "+99 avatars" : "+" + totalAvatars + " avatars";
326+
};
327+
328+
groupWithBtn.addEventListener("ui5-overflow", function (event) {
329+
formatBtnText(event.target);
330+
});
303331
</script>
304332

305333
</body>

0 commit comments

Comments
 (0)