Skip to content

Commit

Permalink
feat(ui5-breadcrumbs): Initial implementation (SAP#3166)
Browse files Browse the repository at this point in the history
  • Loading branch information
kineticjs committed Jun 1, 2021
1 parent 67913d3 commit 81dfb44
Show file tree
Hide file tree
Showing 7 changed files with 517 additions and 0 deletions.
1 change: 1 addition & 0 deletions packages/main/bundle.common.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import "./dist/features/ColorPaletteMoreColors.js";
import Avatar from "./dist/Avatar.js";
import AvatarGroup from "./dist/AvatarGroup.js";
import Badge from "./dist/Badge.js";
import Breadcrumbs from "./dist/Breadcrumbs.js";
import BusyIndicator from "./dist/BusyIndicator.js";
import Button from "./dist/Button.js";
import Card from "./dist/Card.js";
Expand Down
23 changes: 23 additions & 0 deletions packages/main/src/Breadcrumbs.hbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<nav class="ui5-breadcrumbs-root">
<ol class="ui5-breadcrumbs-ol">

<li class="{{overflowClasses}}">
<ui5-link @click="{{_toggleRespPopover}}">
<span>..</span>
<ui5-icon
name="slim-arrow-down"
show-tooltip
accessible-name={{overflowMenuTitle}}></ui5-icon>
</ui5-link>
</li>

{{#each visibleLinks}}
<li class="ui5-breadcrumbs-link" onclick="this.getRootNode().host._onLinkClick(event)">
<slot name="{{this._individualSlot}}"></slot>
</li>
{{/each}}
<li class="ui5-breadcrumbs-current">
<span>{{currentLocation}}</span>
</li>
</ol>
</nav>
297 changes: 297 additions & 0 deletions packages/main/src/Breadcrumbs.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,297 @@
import ItemNavigation from "@ui5/webcomponents-base/dist/delegate/ItemNavigation.js";
import litRender from "@ui5/webcomponents-base/dist/renderer/LitRenderer.js";
import ResizeHandler from "@ui5/webcomponents-base/dist/delegate/ResizeHandler.js";
import NavigationMode from "@ui5/webcomponents-base/dist/types/NavigationMode.js";
import UI5Element from "@ui5/webcomponents-base/dist/UI5Element.js";
import BreadcrumbsSeparatorStyle from "./types/BreadcrumbsSeparatorStyle.js";

// Template
import BreadcrumbsTemplate from "./generated/templates/BreadcrumbsTemplate.lit.js";
import BreadcrumbsPopoverTemplate from "./generated/templates/BreadcrumbsPopoverTemplate.lit.js";

// Styles
import breadcrumbsCss from "./generated/themes/Breadcrumbs.css.js";


/**
* @public
*/
const metadata = {
tag: "ui5-breadcrumbs",
managedSlots: true,
slots: /** @lends sap.ui.webcomponents.main.Breadcrumbs.prototype */ {


/**
* Defines the links of the <code>ui5-breadcrumbs</code>.
* <br><br>
* <b>Note:</b> Use <code>ui5-link</code>. // TODO if precise type can be given in metadata
*
* @type {HTMLElement[]}
* @slot
* @public
*/
"default": {
propertyName: "links",
type: HTMLElement,
individualSlots: true,
},
},
properties: /** @lends sap.ui.webcomponents.main.Breadcrumbs.prototype */ {

/**
* Defines the <code>ui5-breadcrumbs</code> current location text.
*
* @type {string}
* @defaultvalue ""
* @public
*/
currentLocation: {
type: String,
},

/**
* Determines the visual style of the separator between the Breadcrumbs elements.
*
* @type {BreadcrumbsSeparatorStyle}
* @defaultvalue "Slash"
* @public
*/
separatorStyle: {
type: BreadcrumbsSeparatorStyle,
defaultValue: BreadcrumbsSeparatorStyle.Slash,
},

_overflowLength: {
type: String,
noAttribute: true,
}

},
events: /** @lends sap.ui.webcomponents.main.Breadcrumbs.prototype */ {

/**
* Fired when a link is activated.
*
* @event sap.ui.webcomponents.main.Breadcrumbs#item-click
* @param {HTMLElement} item The clicked item.
* @public
*/
"link-click": {
detail: {
link: { type: HTMLElement },
},
},
},
};

/**
* @class
*
* <h3 class="comment-api-title">Overview</h3>
*
*
* @constructor
* @author SAP SE
* @alias sap.ui.webcomponents.main.Breadcrumbs
* @extends UI5Element
* @tagname ui5-breadcrumbs
* @public
*/
class Breadcrumbs extends UI5Element {
static get metadata() {
return metadata;
}

static get render() {
return litRender;
}

static get template() {
return BreadcrumbsTemplate;
}

static get staticAreaTemplate() {
return BreadcrumbsPopoverTemplate;
}

static get styles() {
return breadcrumbsCss;
}

constructor() {
super();
this.initItemNavigation();

this._onResizeHandler = this._onResize.bind(this);
this._onContentResizeHandler = this._onContentResize.bind(this);

this.widthsCache = null;
}

get hasData() {
return this.getSlottedNodes("items").length !== 0;
}

onBeforeRendering() {
this._olDOM && ResizeHandler.deregister(this._olDOM, this._onContentResizeHandler);
}

onAfterRendering() {
this._cacheWidths();
this._applyLayout();
this._olDOM = this.shadowRoot.querySelector(".ui5-breadcrumbs-ol");
ResizeHandler.register(this._olDOM, this._onContentResizeHandler);
}

onEnterDOM() {
ResizeHandler.register(this, this._onResizeHandler);
}

onExitDOM() {
ResizeHandler.deregister(this, this._onResizeHandler);
}

initItemNavigation() {
this._itemNavigation = new ItemNavigation(this, {
navigationMode: NavigationMode.Horizontal,
getItemsCallback: () => this.getSlottedNodes("links"),
});
}

onInvalidation(changeInfo) {
console.log(changeInfo);
}

get shouldShowOverflow() {
return Number(this._overflowLength) > 0;
}

get overflowMenuTitle() {
return "More";
}

_onResize() {
this._applyLayout();
}

_onContentResize() {
this._cacheWidths();
this._applyLayout();
}

_cacheWidths() {
if (!this.widthsCache) {
this.widthsCache = new WeakMap();
}
let map = this.widthsCache;
const linkItems = this.shadowRoot.querySelectorAll(".ui5-breadcrumbs-link");

for(let i = 0; i< linkItems.length; i++) {
let link = linkItems[i].querySelector("slot").assignedElements()[0];
map.set(link, linkItems[i].offsetWidth);
}

if (!this._overflowItemWith) {
const overflow = this.shadowRoot.querySelector(".ui5-breadcrumbs-overflow");
overflow.classList.toggle("hidden", false);
this._overflowItemWith = overflow.clientWidth;
overflow.classList.toggle("hidden", true);
}
}

_applyLayout() {
let iAvailableWidth = this.shadowRoot.querySelector("nav").offsetWidth,
nonOverflowingContentWidth,
links,
newOverflowLength;

if (iAvailableWidth === 0) {
return;
}

nonOverflowingContentWidth = this.shadowRoot.querySelector(".ui5-breadcrumbs-current").clientWidth + this._overflowItemWith;
links = this.getSlottedNodes("links").reverse();
newOverflowLength = links.length;

for(let i = 0; i < links.length ; i++) {
let link = links[i],
linkWidth = this.widthsCache.get(link) || 0;
nonOverflowingContentWidth += linkWidth;
if (nonOverflowingContentWidth > iAvailableWidth) {
break;
}
newOverflowLength--;
if (newOverflowLength === 1) {
nonOverflowingContentWidth -= this._overflowItemWith;
}
}

this._overflowLength = String(newOverflowLength);
}

get _isPickerOpen() {
return !!this.responsivePopover && this.responsivePopover.opened;
}

async _respPopover() {
const staticAreaItem = await this.getStaticAreaItemDomRef();
return staticAreaItem.querySelector("[ui5-responsive-popover]");
}

async _toggleRespPopover() {
this._iconPressed = true;
this.responsivePopover = await this._respPopover();

if (this._isPickerOpen) {
this.responsivePopover.close();
} else {
this.responsivePopover.open(this);
}
}

_onLinkClick(event) {
let link = event.target;
this.fireEvent("link-click", {link});
}

_getSelectedItemIndex(item) {
return [].indexOf.call(item.parentElement.children, item);
}

_onOverflowListItemSelect(event) {
const item = event.detail.item;
const selectedItemIndex = this._getSelectedItemIndex(item);
const selectedLink = this.getSlottedNodes("links")[selectedItemIndex];
window.open(selectedLink.href, selectedLink.target || "_blank");
console.log(selectedLink.href);
this._toggleRespPopover();
this.fireEvent("link-click", {link: selectedLink});
}

get visibleLinks() {
let overflowLength = Number(this._overflowLength) || 0;
return this.getSlottedNodes("links")
.slice(Number(overflowLength));
}

get overflowingLinks() {
let overflowLength = Number(this._overflowLength) || 0;
return this.getSlottedNodes("links").slice(0, overflowLength).reverse();
}
get overflowClasses() {
let classes = "ui5-breadcrumbs-overflow";
if (!this.shouldShowOverflow) {
classes += " hidden";
}
return classes;
}

static get dependencies() {
return [];
}
}

Breadcrumbs.define();

export default Breadcrumbs;
23 changes: 23 additions & 0 deletions packages/main/src/BreadcrumbsPopover.hbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<ui5-responsive-popover
id="{{_id}}-overflowMenu"
horizontal-align="Right"
placement-type="Bottom"
content-only-on-desktop
with-padding
no-arrow
_hide-header>
<ui5-list @ui5-item-press="{{_onOverflowListItemSelect}}">
{{#each overflowingLinks}}
<ui5-li
id="{{this.id}}-li">
{{this.textContent}}
</ui5-li>
{{/each}}
</ui5-list>
<div slot="footer" class="ui5-responsive-popover-footer">
<ui5-button
design="Transparent"
@click="{{_toggleRespPopover}}"
>Cancel</ui5-button>
</div>
</ui5-responsive-popover>
Loading

0 comments on commit 81dfb44

Please sign in to comment.