Skip to content

Commit 449e37f

Browse files
committed
feat(ui5-side-navigation): add indication tag slot
JIRA: BGSOFUIRODOPI-3651
1 parent d40fff6 commit 449e37f

8 files changed

Lines changed: 398 additions & 9 deletions

packages/fiori/src/SideNavigationItem.ts

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,8 @@ import {
2020
SIDE_NAVIGATION_OVERFLOW_ITEM_LABEL,
2121
SIDE_NAVIGATION_PARENT_ITEM_SELECTABLE_DESCRIPTION,
2222
} from "./generated/i18n/i18n-defaults.js";
23-
import type { DefaultSlot } from "@ui5/webcomponents-base/dist/UI5Element.js";
23+
import type { DefaultSlot, Slot } from "@ui5/webcomponents-base/dist/UI5Element.js";
24+
import "@ui5/webcomponents/dist/Tag.js";
2425

2526
// Templates
2627
import SideNavigationItemTemplate from "./SideNavigationItemTemplate.js";
@@ -81,6 +82,16 @@ class SideNavigationItem extends SideNavigationSelectableItemBase {
8182
@slot({ type: HTMLElement, invalidateOnChildChange: true, "default": true })
8283
items!: DefaultSlot<SideNavigationSubItem>;
8384

85+
/**
86+
* Defines the tag to be displayed.
87+
* Only `ui5-tag` component should be used.
88+
*
89+
* @public
90+
* @since 2.7.0
91+
*/
92+
@slot({ type: HTMLElement })
93+
tag!: Slot<HTMLElement>;
94+
8495
@i18n("@ui5/webcomponents-fiori")
8596
static i18nBundle: I18nBundle;
8697

@@ -183,9 +194,21 @@ class SideNavigationItem extends SideNavigationSelectableItemBase {
183194
}
184195

185196
get _describedBy() {
197+
const parts: string[] = [];
198+
199+
if (this.hasTag) {
200+
parts.push(this._tagId);
201+
}
202+
186203
if (!this.effectiveDisabled && this.items.length && !this.unselectable) {
187-
return SideNavigationItem.i18nBundle.getText(SIDE_NAVIGATION_PARENT_ITEM_SELECTABLE_DESCRIPTION, this.text ?? "");
204+
parts.push(SideNavigationItem.i18nBundle.getText(SIDE_NAVIGATION_PARENT_ITEM_SELECTABLE_DESCRIPTION, this.text ?? ""));
188205
}
206+
207+
return parts.length > 0 ? parts.join(" ") : undefined;
208+
}
209+
210+
get hasTag() {
211+
return !!this.tag.length;
189212
}
190213

191214
get classesArray() {

packages/fiori/src/SideNavigationItemTemplate.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,9 @@ function ItemTemplate(this: SideNavigationItem) {
5252
this.icon && <Icon class="ui5-sn-item-icon" name={this.icon}/>
5353
}
5454
<div class="ui5-sn-item-text">{this.text}</div>
55+
{this.hasTag &&
56+
<slot name="tag" id={this._tagId} class="ui5-sn-item-tag-slot"></slot>
57+
}
5558
{this.sideNavCollapsed ?
5659
!!this.items.length &&
5760
<Icon class="ui5-sn-item-toggle-icon"

packages/fiori/src/SideNavigationPopoverTemplate.tsx

Lines changed: 58 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,20 @@ export default function SideNavigationTemplate(this: SideNavigation) {
1717
target={item.target}
1818
title={item.title}
1919
tooltip={item._tooltip}
20-
ref={this.captureRef.bind(item)}
20+
ref={(el: HTMLElement | null) => {
21+
if (el && item.tag.length > 0) {
22+
if (!el.hasAttribute('data-tags-appended')) {
23+
item.tag.forEach((tagEl) => {
24+
const clonedTag = tagEl.cloneNode(true) as HTMLElement;
25+
clonedTag.slot = 'endContent';
26+
el.appendChild(clonedTag);
27+
});
28+
el.setAttribute('data-tags-appended', 'true');
29+
}
30+
}
31+
this.captureRef.bind(item)(el as any);
32+
}}
2133
>
22-
2334
{item.children.length > 0 && !item.unselectable &&
2435
(<NavigationMenuItem
2536
class="ui5-navigation-menu-item-root-parent"
@@ -31,8 +42,21 @@ export default function SideNavigationTemplate(this: SideNavigation) {
3142
target={item.target}
3243
title={item.title}
3344
tooltip={item._tooltip}
34-
ref={this.captureRef.bind(item)}
35-
></NavigationMenuItem>)
45+
ref={(el: HTMLElement | null) => {
46+
if (el && item.tag.length > 0) {
47+
if (!el.hasAttribute('data-tags-appended')) {
48+
item.tag.forEach((tagEl) => {
49+
const clonedTag = tagEl.cloneNode(true) as HTMLElement;
50+
clonedTag.slot = 'endContent';
51+
el.appendChild(clonedTag);
52+
});
53+
el.setAttribute('data-tags-appended', 'true');
54+
}
55+
}
56+
this.captureRef.bind(item)(el as any);
57+
}}
58+
>
59+
</NavigationMenuItem>)
3660
}
3761

3862
{(item as any).items?.map(renderMenuItem)}
@@ -79,7 +103,20 @@ export default function SideNavigationTemplate(this: SideNavigation) {
79103
selected={this._popoverContents.item.selected}
80104
unselectable={this._popoverContents.item.unselectable}
81105
onui5-click={this.handlePopupItemClick}
82-
ref={this.captureRef.bind(this._popoverContents.item)}
106+
ref={(el: HTMLElement | null) => {
107+
if (el && this._popoverContents.item.tag.length > 0) {
108+
// Only append if the element doesn't have our marker
109+
if (!el.hasAttribute('data-tags-appended')) {
110+
this._popoverContents.item.tag.forEach((tagEl) => {
111+
const clonedTag = tagEl.cloneNode(true) as HTMLElement;
112+
clonedTag.slot = 'tag';
113+
el.appendChild(clonedTag);
114+
});
115+
el.setAttribute('data-tags-appended', 'true');
116+
}
117+
}
118+
this.captureRef.bind(this._popoverContents.item)(el as SideNavigationItem | null);
119+
}}
83120
>
84121
{this._popoverContents.subItems.map(item =>
85122
<SideNavigationSubItem
@@ -93,8 +130,22 @@ export default function SideNavigationTemplate(this: SideNavigation) {
93130
selected={item.selected}
94131
unselectable={item.unselectable}
95132
onui5-click={this.handlePopupItemClick}
96-
ref={this.captureRef.bind(item)}
97-
/>
133+
ref={(el: HTMLElement | null) => {
134+
if (el && item.tag.length > 0) {
135+
// Only append if the element doesn't have our marker
136+
if (!el.hasAttribute('data-tags-appended')) {
137+
item.tag.forEach((tagEl) => {
138+
const clonedTag = tagEl.cloneNode(true) as HTMLElement;
139+
clonedTag.slot = 'tag';
140+
el.appendChild(clonedTag);
141+
});
142+
el.setAttribute('data-tags-appended', 'true');
143+
}
144+
}
145+
this.captureRef.bind(item)(el as SideNavigationSubItem | null);
146+
}}
147+
>
148+
</SideNavigationSubItem>
98149
)}
99150
</SideNavigationItem>
100151
</SideNavigation>

packages/fiori/src/SideNavigationSelectableItemBase.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -254,6 +254,10 @@ class SideNavigationSelectableItemBase extends SideNavigationItemBase {
254254
return this.selected;
255255
}
256256

257+
get _tagId() {
258+
return `${this._id}-tag`;
259+
}
260+
257261
_onkeydown(e: KeyboardEvent) {
258262
const isRTL = this.effectiveDir === "rtl";
259263

packages/fiori/src/SideNavigationSubItem.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
import customElement from "@ui5/webcomponents-base/dist/decorators/customElement.js";
22
import jsxRender from "@ui5/webcomponents-base/dist/renderer/JsxRenderer.js";
3+
import slot from "@ui5/webcomponents-base/dist/decorators/slot-strict.js";
4+
import type { Slot } from "@ui5/webcomponents-base/dist/UI5Element.js";
35
import SideNavigationSelectableItemBase from "./SideNavigationSelectableItemBase.js";
46
import SideNavigationSubItemTemplate from "./SideNavigationSubItemTemplate.js";
7+
import "@ui5/webcomponents/dist/Tag.js";
58

69
// Styles
710
import SideNavigationSubItemCss from "./generated/themes/SideNavigationSubItem.css.js";
@@ -30,6 +33,24 @@ import SideNavigationSubItemCss from "./generated/themes/SideNavigationSubItem.c
3033
styles: SideNavigationSubItemCss,
3134
})
3235
class SideNavigationSubItem extends SideNavigationSelectableItemBase {
36+
/**
37+
* Defines the tag to be displayed.
38+
* Only `ui5-tag` component should be used.
39+
*
40+
* @public
41+
* @since 2.7.0
42+
*/
43+
@slot({ type: HTMLElement })
44+
tag!: Slot<HTMLElement>;
45+
46+
get hasTag() {
47+
return !!this.tag.length;
48+
}
49+
50+
get _describedBy() {
51+
return this.hasTag ? this._tagId : undefined;
52+
}
53+
3354
_onkeydown(e: KeyboardEvent) {
3455
super._onkeydown(e);
3556
}

packages/fiori/src/SideNavigationSubItemTemplate.tsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,15 @@ export default function SideNavigationSubItemTemplate(this: SideNavigationSubIte
2323
href={this._href}
2424
target={this._target}
2525
aria-haspopup={this._ariaHasPopup}
26+
aria-describedby={this._describedBy}
2627
>
2728
{this.icon &&
2829
<Icon class="ui5-sn-item-icon" name={this.icon}/>
2930
}
3031
<div class="ui5-sn-item-text">{this.text}</div>
32+
{this.hasTag &&
33+
<slot name="tag" id={this._tagId} class="ui5-sn-item-tag-slot"></slot>
34+
}
3135
{this.isExternalLink &&
3236
<Icon class="ui5-sn-item-external-link-icon"
3337
name={arrowRight}

packages/fiori/src/themes/SideNavigationItemBase.css

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -357,6 +357,41 @@ and there is an additional border that appears on hover. */
357357
padding-inline-end: 0.375rem;
358358
}
359359

360+
.ui5-sn-item-tag-slot {
361+
padding-inline-start: 0.5rem;
362+
padding-inline-end: 0.375rem;
363+
display: inline-flex;
364+
align-items: center;
365+
}
366+
367+
.ui5-sn-item-tag-slot:has(+ .ui5-sn-item-toggle-icon),
368+
.ui5-sn-item-tag-slot:has(+ .ui5-sn-item-external-link-icon) {
369+
padding-inline-end: 0;
370+
}
371+
372+
.ui5-sn-item-tag-slot + .ui5-sn-item-toggle-icon,
373+
.ui5-sn-item-tag-slot + .ui5-sn-item-external-link-icon {
374+
margin-inline-start: 0.375rem;
375+
}
376+
377+
:host([side-nav-collapsed]) .ui5-sn-item-tag-slot {
378+
display: none;
379+
}
380+
381+
:host([side-nav-collapsed]) .ui5-sn-item:not(.ui5-sn-item-active):not(.ui5-sn-item-no-hover-effect):not(.ui5-sn-item-disabled):hover .ui5-sn-item-tag-slot,
382+
:host([side-nav-collapsed]) .ui5-sn-item:not(.ui5-sn-item-active):not(.ui5-sn-item-no-hover-effect):focus .ui5-sn-item-tag-slot {
383+
display: inline-flex;
384+
max-width: 4rem;
385+
}
386+
387+
:host([in-popover]) .ui5-sn-item-tag-slot {
388+
display: inline-flex;
389+
}
390+
391+
.ui5-sn-item-tag-slot::slotted([ui5-tag]) {
392+
max-width: 4rem;
393+
}
394+
360395
:host([side-nav-collapsed]) .ui5-sn-item-with-expander .ui5-sn-item-icon::after {
361396
display: var(--_ui5_side_navigation_triangle_display);
362397
content: "";

0 commit comments

Comments
 (0)